yaxpeax_core/arch/display/
function.rs

1use yaxpeax_arch::Arch;
2use yaxpeax_arch::AddressBase;
3use yaxpeax_arch::AddressDisplay;
4use yaxpeax_arch::LengthedInstruction;
5use arch::DecodeFrom;
6use arch::InstructionSpan;
7use arch::display::BaseDisplay;
8use analyses::static_single_assignment::DataDisplay;
9use analyses::static_single_assignment::SSAValues;
10use analyses::static_single_assignment::SSA;
11use analyses::control_flow::ControlFlowGraph;
12use memory::MemoryRepr;
13use memory::MemoryRange;
14use data::Direction;
15use display::location::{NoHighlights, HighlightList, LocationHighlighter};
16use arch::FunctionQuery;
17use arch::SymbolQuery;
18use arch::FunctionRepr;
19use yaxpeax_arch::ColorSettings;
20
21use std::fmt;
22use std::fmt::Write;
23use std::marker::PhantomData;
24
25/// This view in particular takes optionally-present data flow information, which ends up
26/// restricting the usefulness of this struct to architectures that have SSA information defined.
27/// a handy TODO: here is to either figure out how to have a default Location that specifies no
28/// locations, and a corresponding data flow reference that never returns locations, OR to figure
29/// out if there should be a different `FunctionView` for non-location-enabled architectures.
30///
31/// Anyway the question of highlighting operands is architecture-independent and only requires that
32/// operands are known, which is true by virtue of `Arch` being implemented
33pub struct FunctionView<
34    'a, 'c, 'd, 'e,
35    F: FunctionRepr,
36    Context: FunctionQuery<A::Address>,
37    A: Arch + BaseDisplay<F, Context> + SSAValues,
38    M: MemoryRepr<A> + MemoryRange<A>
39> {
40    pub _function_type: PhantomData<F>,
41    pub data: &'a M,
42    pub ctx: Context,
43    pub fn_graph: &'c ControlFlowGraph<A::Address>,
44    pub ssa: Option<&'d SSA<A>>,
45    pub colors: Option<&'e ColorSettings>,
46    pub highlight_instrs: Vec<A::Address>,
47    pub highlight_locs: Vec<(A::Address, A::Location, Direction)>,
48}
49
50pub trait FunctionDisplay<A: Arch + SSAValues> {
51    fn entrypoint(&self) -> A::Address;
52    fn remove_highlight_instr(&mut self, addr: A::Address);
53    fn add_highlight_instr(&mut self, addr: A::Address);
54    fn add_highlight_loc(&mut self, loc: (A::Address, A::Location, Direction));
55    fn reset_highlight_instrs(&mut self);
56    fn reset_highlight_locs(&mut self);
57    fn view_between(&self, start: Option<A::Address>, end: Option<A::Address>) -> Vec<(A::Address, Vec<String>)>;
58}
59
60pub trait FunctionInstructionDisplay<A: Arch + SSAValues, Context: SymbolQuery<A::Address> + FunctionQuery<A::Address>> {
61    fn display_instruction_in_function<W: fmt::Write, Highlighter: LocationHighlighter<A::Location>>(
62        dest: &mut W,
63        instr: &A::Instruction,
64        address: A::Address,
65        context: &Context,
66        ssa: Option<&SSA<A>>,
67        colors: Option<&ColorSettings>,
68        highlight: &Highlighter
69    ) -> fmt::Result;
70}
71
72
73impl <
74    'a, 'c, 'd, 'e,
75    A,
76    F: FunctionRepr,
77    Context: FunctionQuery<A::Address> + SymbolQuery<A::Address>,
78    M: MemoryRepr<A> + MemoryRange<A>
79> FunctionDisplay<A> for FunctionView<'a, 'c, 'd, 'e, F, Context, A, M> where
80    A: Arch +
81      SSAValues +
82      FunctionInstructionDisplay<A, Context> +
83      DecodeFrom<M> +
84      BaseDisplay<F, Context>,
85{
86    fn entrypoint(&self) -> A::Address {
87        self.fn_graph.entrypoint
88    }
89    fn remove_highlight_instr(&mut self, addr: A::Address) {
90        for i in 0..self.highlight_instrs.len() {
91            if self.highlight_instrs[i] == addr {
92                self.highlight_instrs.swap_remove(i);
93                return;
94            }
95        }
96    }
97    fn add_highlight_instr(&mut self, addr: A::Address) {
98        self.highlight_instrs.push(addr);
99    }
100    fn add_highlight_loc(&mut self, loc: (A::Address, A::Location, Direction)) {
101        self.highlight_locs.push(loc);
102    }
103    fn reset_highlight_instrs(&mut self) {
104        self.highlight_instrs.clear();
105    }
106    fn reset_highlight_locs(&mut self) {
107        self.highlight_locs.clear();
108    }
109
110    fn view_between(&self, start: Option<A::Address>, end: Option<A::Address>) -> Vec<(A::Address, Vec<String>)> {
111        let mut text: Vec<(A::Address, Vec<String>)> = Vec::new();
112        let mut blocks: Vec<A::Address> = self.fn_graph.blocks.keys().cloned().collect();
113        blocks.sort();
114
115        for blockaddr in blocks.iter() {
116            let block = self.fn_graph.get_block(*blockaddr);
117
118            // hack to avoid looking at the "basic block" over [0, ... first real basic block)
119            // shouldn't be necessary anymore here since `fn_graph` is by definition blocks only in
120            // this function?
121            use num_traits::Zero;
122            if block.start == A::Address::zero() { continue; }
123
124            let mut iter = A::instructions_spanning(self.data, block.start, block.end);
125
126            if let Some(ssa) = self.ssa {
127                let start_ok = if let Some(start) = start {
128                    block.start >= start
129                } else { true };
130
131                let end_ok = if let Some(end) = end {
132                    block.start <= end
133                } else { true };
134
135                if start_ok && end_ok {
136                    if self.fn_graph.sources(block.start).len() == 0 {
137                        continue;
138                    }
139                    let mut strings: Vec<String> = Vec::new();
140
141                    // this is the start of the block, so also check for Between's and phis
142                    for source in self.fn_graph.sources(block.start) {
143                        if let Some(modifications) = ssa.control_dependent_values
144                            .get(&source)
145                            .and_then(|dests| dests.get(&block.start)) {
146
147                            for ((_loc, dir), value) in modifications.iter() {
148                                match dir {
149                                    Direction::Read => {
150                                        strings.push(format!("read: {}", value.borrow().display(false, None)));
151                                        strings.push(format!("  via edge {} -> {}", source.show(), block.start.show()));
152                                    }
153                                    Direction::Write => {
154                                        strings.push(format!("write: {}", value.borrow().display(false, None)));
155                                        strings.push(format!("  via edge {} -> {}", source.show(), block.start.show()));
156                                    }
157                                }
158                            }
159                        }
160                    }
161
162                    if let Some(phis) = ssa.phi.get(&block.start) {
163                        // TODO rephrase this to A::blank_frame(addr).
164                        let frame = format!("{}:                                 : | |", block.start.show());
165                        let mut hiddens: Vec<A::Location> = Vec::new();
166                        for (_, phi_op) in phis.iter() {
167                            let out = phi_op.out.borrow();
168                            if !out.used {
169                                hiddens.push(out.location.clone());
170                                continue;
171                            }
172                            let mut phi_line = format!("{} {} <- phi(", frame, phi_op.out.borrow().display(false, None));
173                            let mut in_iter = phi_op.ins.iter();
174                            if let Some(phi_in) = in_iter.next() {
175                                write!(phi_line, "{}", phi_in.borrow().display(false, None)).unwrap();
176                            }
177                            while let Some(phi_in) = in_iter.next() {
178                                write!(phi_line, ", {}", phi_in.borrow().display(false, None)).unwrap();
179                            }
180                            phi_line.push(')');
181                            strings.push(phi_line);
182                        }
183                        if hiddens.len() > 0 {
184                            let mut hidden_locs_line = format!("{} hidden dead phis for {:?}", frame, hiddens[0]);
185                            for loc in &hiddens[1..] {
186                                write!(hidden_locs_line, ", {:?}", loc).unwrap();
187                            }
188                            strings.push(hidden_locs_line);
189                        }
190                    }
191                    text.push((block.start, strings));
192                }
193            }
194
195            let span_end = iter.end;
196            while let Some((address, instr)) = iter.next() {
197                if let Some(start) = start {
198                    if address < start {
199                        continue;
200                    }
201                }
202
203                if let Some(end) = end {
204                    if address > end {
205                        continue;
206                    }
207                }
208
209                let mut instr_string = String::new();
210                A::render_frame(
211                    &mut instr_string,
212                    address,
213                    instr,
214                    &mut self.data.range(address..(address.wrapping_offset(instr.len()))).unwrap(),
215                    Some(&self.ctx),
216                ).unwrap();
217                // ok come back to this
218                write!(instr_string, " ").unwrap();
219                if self.highlight_instrs.contains(&address) {
220                    write!(instr_string, "{}", termion::style::Invert).unwrap();
221                }
222                let highlights: Vec<(A::Location, Direction)> = self.highlight_locs.iter().filter_map(|(highlight_addr, loc, dir)| {
223                    Some((loc.clone(), *dir)).filter(|_| highlight_addr == &address)
224                }).collect();
225                if highlights.len() == 0 {
226                    A::display_instruction_in_function(
227                        &mut instr_string,
228                        &instr,
229                        address,
230                        &self.ctx, self.ssa, self.colors,
231                        &NoHighlights
232                    ).unwrap();
233                } else {
234                    A::display_instruction_in_function(
235                        &mut instr_string,
236                        &instr,
237                        address,
238                        &self.ctx, self.ssa, self.colors,
239                        &HighlightList {
240                            operands_bits: 0,
241                            location_highlights: highlights
242                        }
243                    ).unwrap();
244                }
245                if self.highlight_instrs.contains(&address) {
246                    write!(instr_string, "{}", termion::style::NoInvert).unwrap();
247                }
248                if address.wrapping_offset(instr.len()) > span_end {
249                    write!(instr_string ,"\n┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄").unwrap();
250                }
251
252                // we might have to append after phis
253                match text.last().map(|(addr, _)| *addr) {
254                    Some(last_addr) if last_addr == address => { /* great! we already have a vec to insert in to */ },
255                    _ => {
256                        text.push((address, Vec::new()));
257                    }
258                }
259                text.last_mut().expect("actually unreachable").1.extend(
260                    instr_string.split("\n").map(|s| s.to_string())
261                );
262            }
263        }
264        text
265    }
266}
267