breakpad_symbols/sym_file/
walker.rs

1//! This module implements support for breakpad's text-based STACK CFI and STACK WIN
2//! unwinding instructions. This isn't something you need to actually use
3//! directly, it's just public so these docs will get a nice pretty rendering.
4//!
5//! The rest of this documentation is discussion of STACK CFI and STACK WIN
6//! format -- both how to parse and evaluate them.
7//!
8//! Each STACK line provides instructions on how to unwind the program at
9//! a given instruction address. Specifically this means how to restore
10//! registers, which most importantly include the instruction pointer ($rip/$eip/pc)
11//! and stack pointer ($rsp/$esp/sp).
12//!
13//! STACK WIN lines are completely self-contained while STACK CFI lines may
14//! depend on the lines above them.
15//!
16//! Note that all addresses are relative to the start of the module -- resolving
17//! the module and applying that offset is left as an exercise to the reader.
18//!
19//! See also [the upstream breakpad docs](https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md)
20//! which are *ok* but can be a bit hazy around the details (I think they've
21//! just partially bitrotted). To the best of my ability I have tried to make
22//! these docs as accurate and detailed as possible.
23//!
24//! I also try to be honest about the places where I'm uncertain about the
25//! semantics.
26//!
27//!
28//!
29//!
30//!
31//! # Known Differences Between This Implementation and Breakpad
32//!
33//! I haven't thoroughly tested the two implementations for compatibility,
34//! but where I have noticed a difference I'll put it here so it's
35//! documented *somewhere*.
36//!
37//!
38//!
39//! ## Register Names
40//!
41//! Breakpad assumes register names are prefixed with `$` *EXCEPT*
42//! on ARM variants. These prefixes are hardcoded, so if you hand it
43//! `$rax` or `x11` it will be happy, but if you hand it `rax` or `$x11`
44//! it will freak out and be unable to parse the CFI expressions.
45//!
46//! This implementation doesn't have any notion of "valid" registers
47//! for a particular execution, and so just unconditionally strips leading
48//! $'s. So `$rax`, `$x11`, `rax`, and `x11` should all be valid.
49//!
50//! Registers names are otherwise only "validated" by the [FrameWalker][],
51//! in that it will return an error if we try to get or set a register name
52//! *it* doesn't recognize (or doesn't have a valid value for). But it
53//! doesn't ever expect `$`'s, so that detail has been erased by
54//! the time it's involved.
55//!
56//! The author of this document may or may not know this as a result of
57//! accidentally causing mozilla/dump_syms to emit `$x11` in some situations.
58//! If that is the case, they fixed it, so everything's fine, right?
59//!
60//! It's bad to be a permissive parser, but symbol files are already
61//! an inconsistent mess, so you kind of *have* to be permissive in random
62//! places? And we don't have a conformance test suite to keep everything
63//! perfectly bug-compatible with breakpad when it doesn't document
64//! everything enough to know what's "intended".
65//!
66//!
67//!
68//! ## cfi_scan hacks
69//!
70//! This is technically a technique that the *user* of walker.rs would
71//! implement, but it's worth discussing here since it relates to cfi
72//! evaluation.
73//!
74//! When evaluating STACK WIN expressions, breakpad will apply several
75//! heuristics to adjust values. This includes scanning the stack to
76//! try to "refine" the inputs and outputs.
77//!
78//! At the moment, we implement very few of these heuristics. We definitely
79//! don't do any scanning when evaluating STACK WIN.
80//!
81//! The ones we *do* implement (and that I can recall) are:
82//!
83//! * changing the value of searchStart based on whether the program
84//!   includes an `@`.
85//!
86//! * trying to forward the value of `$ebx` in more situations
87//!   than the STACK WIN suggests you should.
88//!
89//! At this point I don't recall if these were implemented to fix actual
90//! issues found during development, or if I just cargo-culted them
91//! because they seemed relatively inoffensive.
92//!
93//!
94//!
95//!
96//!
97//! # STACK CFI
98//!
99//! STACK CFI lines comes in two forms:
100//!
101//! `STACK CFI INIT instruction_address num_bytes registers`
102//!
103//! `STACK CFI instruction_address registers`
104//!
105//!
106//! A `STACK CFI INIT` line specifies how to restore registers for the given
107//! range of addresses.
108//!
109//! Example: `STACK CFI INIT 804c4b0 40 .cfa: $esp 4 + $eip: .cfa 4 - ^`
110//!
111//! Arguments:
112//!   * instruction_address (hex u64) is the first address in the module this line applies to
113//!   * num_bytes (hex u64) is the number of bytes it (and its child STACK CFI lines) covers
114//!   * registers (string) is the register restoring instructions (see the next section)
115//!
116//!
117//! A `STACK CFI` line always follows a "parent" `STACK CFI INIT` line. It
118//! updates the instructions on how to restore registers for anything within
119//! the parent STACK CFI INIT's range after the given address (inclusive).
120//! It only specifies rules for registers that have new instructions.
121//!
122//! To get the final rules for a given address, start with its `STACK CFI INIT`
123//! and then apply all the applicable `STACK CFI` "diffs" in order.
124//!
125//! Example: `STACK CFI 804c4b1 .cfa: $esp 8 + $ebp: .cfa 8 - ^`
126//!
127//! Arguments:
128//!   * instruction_address (hex u64) is the first address to apply these instructions
129//!   * registers (string) is the new register restoring instructions (see the next section)
130//!
131//!
132//!
133//! ## STACK CFI registers
134//!
135//! A line's STACK CFI registers are of the form
136//!
137//! `REG: EXPR REG: EXPR REG: EXPR...`
138//!
139//! Where REG is `.cfa`, `.ra`, `$<alphanumeric>`, or `<alphanumeric>`
140//! (but not a valid integer literal).
141//!
142//! And EXPR is `<anything but ":">` (see next section for details)
143//!
144//! Each `REG: EXPR` pair specifies how to compute the register REG for the
145//! caller. There are three kinds of registers:
146//!
147//! * `$XXX` or `XXX` refers to an actual general-purpose register. In REG position it
148//!   refers to the caller, in an EXPR it refers to the callee. Register names
149//!   can in theory be any alphanumeric string that isn't a valid integer literal.
150//!   e.g. `$rax`, `x11`. `$` prefixes are expected for all platforms except ARM
151//!   variants. This parser is more permissive and allows for either form on all
152//!   platforms. Completely invalid register names (`x99`) will be caught at evaluation time.
153//!
154//! * `.cfa` is the "canonical frame address" (CFA), as used in DWARF CFI. It
155//!   abstractly represents the base address of the frame. On x86, x64, and
156//!   ARM64 the CFA is the caller's stack pointer from *before* the call. As
157//!   such on those platforms you will never see instructions to restore the
158//!   frame pointer -- it must be implicitly restored from the cfa. `.cfa`
159//!   always refers to the caller, and therefore must be computed without
160//!   use of itself.
161//!
162//! * `.ra` is the "return address", which just abstractly refers to the
163//!   instruction pointer/program counter. It only ever appears in REG
164//!   position.
165//!
166//! `.cfa` and `.ra` must always have defined rules, or the STACK CFI is malformed.
167//!
168//! The CFA is special because its computed value can be used by every other EXPR.
169//! As such it should always be computed first so that its value is available.
170//! The purpose of the CFA is to cleanly handle the very common case of registers
171//! saved to the stack. Every register saved this way lives at a fixed offset
172//! from the start of the frame. So we can specify their rules once, and just
173//! update the CFA.
174//!
175//! For example:
176//!
177//! ```text
178//! STACK CFI INIT 0x10 16 .cfa: $rsp 8 + .ra: .cfa -8 + ^
179//! STACK CFI 0x11 .cfa: $rsp 16 + $rax: .cfa -16 + ^
180//! STACK CFI 0x12 .cfa: $rsp 24 +
181//! ```
182//!
183//! Can be understood as (pseudo-rust):
184//!
185//! ```rust,ignore
186//! let mut cfa = 0;
187//! let mut ra = None;
188//! let mut caller_rax = None;
189//!
190//!
191//! // STACK CFI INIT 0x10's original state
192//! cfa = callee_rsp + 8;
193//! ra = Some(|| { *(cfa - 8) });            // Defer evaluation
194//!
195//!
196//! // STACK CFI 0x11's diff
197//! if address >= 0x11 {
198//!   cfa = callee_rsp + 16;
199//!   caller_rax = Some(|| { *(cfa - 16) }); // Defer evaluation
200//! }
201//!
202//!
203//! // STACK CFI 0x12's diff
204//! if address >= 0x12 {
205//!   cfa = callee_rsp + 24;
206//! }
207//!
208//! caller.stack_pointer = cfa;
209//!
210//! // Finally evaluate all other registers using the current cfa
211//! caller.instruction_pointer = ra.unwrap()();
212//! caller.rax = caller_rax.map(|func| func());
213//! ```
214//!
215//!
216//!
217//! ## STACK CFI expressions
218//!
219//! STACK CFI expressions are in postfix (Reverse Polish) notation with tokens
220//! separated by whitespace. e.g.
221//!
222//! ```text
223//! .cfa $rsp 3 + * ^
224//! ```
225//!
226//! Is the postfix form of
227//!
228//! ```text
229//! ^(.cfa * ($rsp + 3))
230//! ```
231//!
232//! The benefit of postfix notation is that it can be evaluated while
233//! processing the input left-to-right without needing to maintain any
234//! kind of parse tree.
235//!
236//! The only state a postfix evaluator needs to maintain is a stack of
237//! computed values. When a value (see below) is encountered, it is pushed
238//! onto the stack. When an operator (see below) is encountered, it can be
239//! evaluated immediately by popping its inputs off the stack and pushing
240//! its output onto the stack.
241//!
242//! If the postfix expression is valid, then at the end of the token
243//! stream the stack should contain a single value, which is the result.
244//!
245//! For binary operators the right-hand-side (rhs) will be the first
246//! value popped from the stack.
247//!
248//! Supported operations are:
249//!
250//! * `+`: Binary Add
251//! * `-`: Binary Subtract
252//! * `*`: Binary Multiply
253//! * `/`: Binary Divide
254//! * `%`: Binary Remainder
255//! * `@`: Binary Align (truncate lhs to be a multiple of rhs)
256//! * `^`: Unary Dereference (load from stack memory)
257//!
258//! Supported values are:
259//!
260//! * `.cfa`: read the CFA
261//! * `.undef`: terminate execution, the output is explicitly unknown
262//! * `<a signed decimal integer>`: read this integer constant (limited to i64 precision)
263//! * `$<alphanumeric>`: read a general purpose register from the callee's frame
264//! * `<alphanumeric>`: same as above (can't be an integer literal)
265//!    
266//! Whether registers should be `$reg` or `reg` depends on the platform.
267//! This parser is permissive, and just accepts both on all platforms.
268//!
269//! But I believe `$` is "supposed" to be used on every platform except for
270//! ARM variants.
271//!
272//!
273//!
274//! # STACK WIN
275//!
276//! STACK WIN lines try to encode the more complex unwinding rules produced by
277//! x86 Windows toolchains. On any other target (x64 windows, x86 linux, etc),
278//! only STACK CFI should be used. This is a good thing, because STACK WIN is
279//! a bit of a hacky mess, as you'll see.
280//!
281//!
282//! ```text
283//! STACK WIN type instruction_address num_bytes prologue_size epilogue_size parameter_size
284//!           saved_register_size local_size max_stack_size has_program_string
285//!           program_string_OR_allocates_base_pointer
286//! ```
287//!
288//!
289//! Examples:
290//!
291//! ```text
292//! STACK WIN 4 a1080 fa 9 0 c 0 0 0 1 $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + =`
293//!
294//! STACK WIN 0 1cab960 68 0 0 10 0 8 0 0 0
295//! ```
296//!
297//!
298//! Arguments:
299//!   * type is either 4 ("framedata") or 0 ("fpo"), see their sections below
300//!   * instruction_address (hex u64) is the first address in the module this line applies to
301//!   * num_bytes (hex u64) is the number of bytes it covers
302//!   * has_program_string (0 or 1) indicates the meaning of the next argument (implied by type?)
303//!   * program_string_OR_allocates_base_pointer is one of:
304//!      * program_string (string) is the expression to evaluate for "framedata" (see that section)
305//!      * allocates_base_pointer (0 or 1) whether ebp is pushed for "fpo" (see that section)
306//!
307//! The rest of the arguments are just values you may need to use in the STACK WIN
308//! evaluation algorithms:
309//!
310//!   * prologue_size
311//!   * epilogue_size
312//!   * parameter_size
313//!   * saved_register_size
314//!   * local_size
315//!   * max_stack_size
316//!
317//! Two useful values derived from these values are:
318//!
319//! ```rust,ignore
320//! grand_callee_parameter_size = callee.parameter_size
321//! frame_size = local_size + saved_register_size + grand_callee_parameter_size
322//! ```
323//!
324//! Having frame_size allows you to find the offset from $esp to the return
325//! address (and other saved registers). This requires grand_callee_parameter_size
326//! because certain windows calling conventions makes the caller responsible for
327//! destroying the callee's arguments, which means they are part of the caller's
328//! frame, and therefore change the offset to the return address. (During unwinding
329//! we generally refer to the current frame as the "callee" and the next frame as
330//! the "caller", but here we're concerned with callee's callee, hence grand_callee.)
331//!
332//! Note that grand_callee_paramter_size is using the STACK WIN entry of the
333//! *previous* frame. Although breakpad symbol files have FUNC entries which claim
334//! to provide parameter_size as well, those values are not to be trusted (or
335//! at least, the grand-callee's STACK WIN entry is to be preferred). The two
336//! values are frequently different, and the STACK WIN ones are more accurate.
337//!
338//! If there is no grand_callee (i.e. you are unwinding the first frame of the
339//! stack), grand_callee_parameter_size can be defaulted to 0.
340//!
341//!
342//!
343//!
344//! # STACK WIN frame pointer mode ("fpo")
345//!
346//! This is an older mode that just gives you minimal information to unwind:
347//! the size of the stack frame (`frame_size`). All you can do is find the
348//! return address, update `$esp`, and optionally restore `$ebp` (if allocates_base_pointer).
349//!
350//! This is best described by pseudocode:
351//!
352//! ```text
353//!   $eip := *($esp + frame_size)
354//!
355//!   if allocates_base_pointer:
356//!     // $ebp was being used as a general purpose register, old value saved here
357//!     $ebp := *($esp + grand_callee_parameter_size + saved_register_size - 8)
358//!   else:
359//!     // Assume both ebp and ebx are preserved (if they were previously valid)
360//!     $ebp := $ebp
361//!     $ebx := $ebx
362//!
363//!   $esp := $esp + frame_size + 4
364//! ```
365//!
366//! I don't have an interesting explanation for why that position is specifically
367//! where $ebp is saved, it just is. The algorithm tries to forward $ebx when $ebp
368//! wasn't messed with as a bit of a hacky way to encourage certain Windows system
369//! functions to unwind better. Evidently some of them have framedata expressions
370//! that depend on $ebx, so preserving it whenever it's plausible is desirable?
371//!
372//!
373//!
374//!
375//! # STACK WIN expression mode ("framedata")
376//!
377//! This is the general purpose mode that has you execute a tiny language to compute
378//! arbitrary registers.
379//!
380//! STACK WIN expressions use many of the same concepts as STACK CFI, but rather
381//! than using `REG: EXPR` pairs to specify outputs, it maintains a map of variables
382//! whose values can be read and written by each expression.
383//!
384//! I personally find this easiest to understand as an extension to the STACK CFI
385//! expressions, so I'll describe it in those terms:
386//!
387//! The supported operations add one binary operation:
388//!
389//! * `=`: Binary Assign (assign the rhs's integer to the lhs's variable)
390//!
391//! This operation requires us to have a distinction between *integers* and
392//! *variables*, which the postfix evaluator's stack must hold.
393//!
394//! All other operators operate only on integers. If a variable is passed where
395//! an integer is expected, that means the current value of the variable should
396//! be used.
397//!
398//! "values" then become:
399//!
400//! * `.<alphanumeric>`: a variable containing some initial constants (see below)
401//! * `$<alphanumeric>`: a variable representing a general purpose register or temporary
402//! * `<alphanumeric>`: same as above, but can't be an integer literal
403//! * `.undef`: delete the variable if this is assigned to it (like Option::None)
404//! * `<a signed decimal integer>`: read this integer constant (limited to i64 precision)
405//!
406//!
407//! Before evaluating a STACK WIN expression:
408//!
409//! * The variables `$ebp` and `$esp` should be initialized from the callee's
410//!   values for those registers (error out if those are unknown). `$ebx` should
411//!   similarly be initialized if it's available, since some things use it, but
412//!   it's optional.
413//!
414//! * The following constant variables should be set accordingly:
415//!   * `.cbParams = parameter_size`
416//!   * `.cbCalleeParams = grand_callee_parameter_size` (only for breakpad-generated exprs?)
417//!   * `.cbSavedRegs = saved_register_size`
418//!   * `.cbLocals = local_size`
419//!   * `.raSearch = $esp + frame_size`
420//!   * `.raSearchStart = .raSearch` (synonym that sometimes shows up?)
421//!
422//! Note that `.raSearch(Start)` roughly corresponds to STACK CFI's `.cfa`, in that
423//! it generally points to where the return address is. However breakpad seems to
424//! believe there are many circumstances where this value can be slightly wrong
425//! (due to the frame pointer having mysterious extra alignment?). As such,
426//! breakpad has several messy heuristics to "refine" `.raSearchStart`, such as
427//! scanning the stack. This implementation does not (yet?) implement those
428//! heuristics. As of this writing I have not encountered an instance of this
429//! problem in the wild (but I haven't done much testing!).
430//!
431//!
432//! After evaluating a STACK WIN expression:
433//!
434//! The caller's registers are stored in `$eip`, `$esp`, `$ebp`, `$ebx`, `$esi`,
435//! and `$edi`. If those variables are undefined, then their values in the caller
436//! are unknown. Do not implicitly forward registers that weren't explicitly set.
437//!
438//! (Should it be an error if the stack isn't empty at the end? It's
439//! arguably malformed input but also it doesn't matter since the output is
440//! in the variables? *shrug*)
441//!
442//!
443//!
444//! ## Example STACK WIN framedata evaluation
445//!
446//! Here is an example of framedata for a function with the standard prologue.
447//! Given the input:
448//!
449//! ```text
450//! $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =
451//! ```
452//!
453//! and initial state:
454//!
455//! ```text
456//! ebp: 16, esp: 1600
457//! ```
458//!
459//! Then evaluation proceeds as follows:
460//!
461//! ```text
462//!   Token  |    Stack     |                       Vars
463//! ---------+--------------+----------------------------------------------------
464//!          |              | $ebp: 16,      $esp: 1600,
465//!   $T0    | $T0          | $ebp: 16,      $esp: 1600,
466//!   $ebp   | $T0 $ebp     | $ebp: 16,      $esp: 1600,
467//!   =      |              | $ebp: 16,      $esp: 1600,   $T0: 16,
468//!   $eip   | $eip         | $ebp: 16,      $esp: 1600,   $T0: 16,
469//!   $T0    | $eip $T0     | $ebp: 16,      $esp: 1600,   $T0: 16,
470//!   4      | $eip $T0 4   | $ebp: 16,      $esp: 1600,   $T0: 16,
471//!   +      | $eip 20      | $ebp: 16,      $esp: 1600,   $T0: 16,
472//!   ^      | $eip (*20)   | $ebp: 16,      $esp: 1600,   $T0: 16,
473//!   =      |              | $ebp: 16,      $esp: 1600,   $T0: 16,   $eip: (*20)
474//!   $ebp   | $ebp         | $ebp: 16,      $esp: 1600,   $T0: 16,   $eip: (*20)
475//!   $T0    | $ebp $T0     | $ebp: 16,      $esp: 1600,   $T0: 16,   $eip: (*20)
476//!   ^      | $ebp (*16)   | $ebp: 16,      $esp: 1600,   $T0: 16,   $eip: (*20)
477//!   =      |              | $ebp: (*16),   $esp: 1600,   $T0: 16,   $eip: (*20)
478//!   $esp   | $esp         | $ebp: (*16),   $esp: 1600,   $T0: 16,   $eip: (*20)
479//!   $T0    | $esp $T0     | $ebp: (*16),   $esp: 1600,   $T0: 16,   $eip: (*20)
480//!   8      | $esp $T0 8   | $ebp: (*16),   $esp: 1600,   $T0: 16,   $eip: (*20)
481//!   +      | $esp 24      | $ebp: (*16),   $esp: 1600,   $T0: 16,   $eip: (*20)
482//!   =      |              | $ebp: (*16),   $esp: 24,     $T0: 16,   $eip: (*20)
483//! ```
484//!
485//! Giving a final output of `ebp=(*16)`, `esp=24`, `eip=(*20)`.
486
487use super::{CfiRules, StackInfoWin, WinStackThing};
488use crate::FrameWalker;
489use std::collections::HashMap;
490use std::str::FromStr;
491use tracing::{debug, trace};
492
493pub fn walk_with_stack_cfi(
494    init: &CfiRules,
495    additional: &[CfiRules],
496    walker: &mut dyn FrameWalker,
497) -> Option<()> {
498    trace!("trying STACK CFI exprs");
499    trace!("  {}", init.rules);
500    for line in additional {
501        trace!("  {}", line.rules);
502    }
503
504    // First we must collect up all the `REG: EXPR` pairs in these lines.
505    // If a REG occurs twice, we prefer the one that comes later. This allows
506    // STACK CFI records to apply incremental updates to the instructions.
507    let mut exprs = HashMap::new();
508    parse_cfi_exprs(&init.rules, &mut exprs)?;
509    for line in additional {
510        parse_cfi_exprs(&line.rules, &mut exprs)?;
511    }
512    trace!("STACK CFI parse successful");
513
514    // These two are special and *must* always be present
515    let cfa_expr = exprs.remove(&CfiReg::Cfa)?;
516    let ra_expr = exprs.remove(&CfiReg::Ra)?;
517    trace!("STACK CFI seems reasonable, evaluating");
518
519    // Evaluating the CFA cannot itself use the CFA
520    let cfa = eval_cfi_expr(cfa_expr, walker, None)?;
521    trace!("successfully evaluated .cfa (frame address)");
522    let ra = eval_cfi_expr(ra_expr, walker, Some(cfa))?;
523    trace!("successfully evaluated .ra (return address)");
524
525    walker.set_cfa(cfa)?;
526    walker.set_ra(ra)?;
527
528    for (reg, expr) in exprs {
529        if let CfiReg::Other(reg) = reg {
530            // If this eval fails, just don't emit this particular register
531            // and keep going on. It's fine to lose some general purpose regs,
532            // but make sure to clear it in case it would have been implicitly
533            // forwarded from the callee.
534            match eval_cfi_expr(expr, walker, Some(cfa)) {
535                Some(val) => {
536                    walker.set_caller_register(reg, val);
537                    trace!("successfully evaluated {}", reg);
538                }
539                None => {
540                    walker.clear_caller_register(reg);
541                    trace!("optional register {} failed to evaluate, dropping it", reg);
542                }
543            }
544        } else {
545            // All special registers should already have been removed??
546            unreachable!()
547        }
548    }
549
550    Some(())
551}
552
553fn parse_cfi_exprs<'a>(input: &'a str, output: &mut HashMap<CfiReg<'a>, &'a str>) -> Option<()> {
554    // Note this is an ascii format so we can think chars == bytes!
555
556    let base_addr = input.as_ptr() as usize;
557    let mut cur_reg = None;
558    let mut expr_first: Option<&str> = None;
559    let mut expr_last: Option<&str> = None;
560    for token in input.split_ascii_whitespace() {
561        if let Some(token) = token.strip_suffix(':') {
562            // This token is a "REG:", indicating the end of the previous EXPR
563            // and start of the next. If we already have an active register,
564            // then now is the time to commit it to our output.
565            if let Some(reg) = cur_reg {
566                // We compute the the expr substring by just abusing the fact that rust substrings
567                // point into the original string, so we can use map addresses in the substrings
568                // back into indices into the original string.
569                let min_addr = expr_first?.as_ptr() as usize;
570                let max_addr = expr_last?.as_ptr() as usize + expr_last?.len();
571                let expr = &input[min_addr - base_addr..max_addr - base_addr];
572
573                // Intentionally overwrite any pre-existing entries for this register,
574                // because that's how CFI records work.
575                output.insert(reg, expr);
576
577                expr_first = None;
578                expr_last = None;
579            }
580
581            cur_reg = if token == ".cfa" {
582                Some(CfiReg::Cfa)
583            } else if token == ".ra" {
584                Some(CfiReg::Ra)
585            } else if let Some(token) = token.strip_prefix('$') {
586                // x86-style $rax register
587                Some(CfiReg::Other(token))
588            } else {
589                // arm-style x11 register
590                Some(CfiReg::Other(token))
591            };
592        } else {
593            // First token *must* be a register!
594            cur_reg.as_ref()?;
595
596            // This is just another part of the current EXPR, update first/last accordingly.
597            if expr_first.is_none() {
598                expr_first = Some(token);
599            }
600            expr_last = Some(token);
601        }
602    }
603
604    // Process the final rule (there must be a defined reg!)
605    let min_addr = expr_first?.as_ptr() as usize;
606    let max_addr = expr_last?.as_ptr() as usize + expr_last?.len();
607    let expr = &input[min_addr - base_addr..max_addr - base_addr];
608
609    output.insert(cur_reg?, expr);
610
611    Some(())
612}
613
614fn eval_cfi_expr(expr: &str, walker: &mut dyn FrameWalker, cfa: Option<u64>) -> Option<u64> {
615    // FIXME: this should be an ArrayVec or something, most exprs are simple.
616    let mut stack: Vec<u64> = Vec::new();
617    for token in expr.split_ascii_whitespace() {
618        match token {
619            // FIXME?: not sure what overflow/sign semantics are, but haven't run into
620            // something where it actually matters (I wouldn't expect it to come up
621            // normally?).
622            "+" => {
623                // Add
624                let rhs = stack.pop()?;
625                let lhs = stack.pop()?;
626                stack.push(lhs.wrapping_add(rhs));
627            }
628            "-" => {
629                // Subtract
630                let rhs = stack.pop()?;
631                let lhs = stack.pop()?;
632                stack.push(lhs.wrapping_sub(rhs));
633            }
634            "*" => {
635                // Multiply
636                let rhs = stack.pop()?;
637                let lhs = stack.pop()?;
638                stack.push(lhs.wrapping_mul(rhs));
639            }
640            "/" => {
641                // Divide
642                let rhs = stack.pop()?;
643                let lhs = stack.pop()?;
644                if rhs == 0 {
645                    // Div by 0
646                    return None;
647                }
648                stack.push(lhs.wrapping_div(rhs));
649            }
650            "%" => {
651                // Remainder
652                let rhs = stack.pop()?;
653                let lhs = stack.pop()?;
654                if rhs == 0 {
655                    // Div by 0
656                    return None;
657                }
658                stack.push(lhs.wrapping_rem(rhs));
659            }
660            "@" => {
661                // Align (truncate)
662                let rhs = stack.pop()?;
663                let lhs = stack.pop()?;
664
665                if rhs == 0 || !rhs.is_power_of_two() {
666                    return None;
667                }
668
669                // ~Bit Magic Corner~
670                //
671                // A power of two has only one bit set (e.g. 4 is 0b100), and
672                // subtracting 1 from that gets you all 1's below that bit (e.g. 0b011).
673                // -1 is all 1's.
674                //
675                // So XORing -1 with (power_of_2 - 1) gets you all ones except
676                // for the bits lower than the power of 2. ANDing that value
677                // to a number consequently makes it a multiple of that power
678                // of two (all the bits smaller than the power are cleared).
679                stack.push(lhs & (-1i64 as u64 ^ (rhs - 1)))
680            }
681            "^" => {
682                // Deref the value
683                let ptr = stack.pop()?;
684                stack.push(walker.get_register_at_address(ptr)?);
685            }
686            ".cfa" => {
687                // Push the CFA. Note the CFA shouldn't be used to compute
688                // itself, so this returns None if that happens.
689                stack.push(cfa?);
690            }
691            ".undef" => {
692                // This register is explicitly undefined!
693                return None;
694            }
695            _ => {
696                // More complex cases
697                if let Some((_, reg)) = token.split_once('$') {
698                    // Push a register
699                    stack.push(walker.get_callee_register(reg)?);
700                } else if let Ok(value) = i64::from_str(token) {
701                    // Push a constant
702                    // FIXME?: We do everything in wrapping arithmetic, so it's
703                    // probably fine to squash i64's into u64's, but it seems sketchy?
704                    // Division/remainder in particular seem concerning, but also
705                    // it would be surprising to see negatives for those..?
706                    stack.push(value as u64)
707                } else if let Some(reg) = walker.get_callee_register(token) {
708                    // Maybe the register just didn't have a $ prefix?
709                    // (seems to be how ARM syntax works).
710                    stack.push(reg);
711                } else {
712                    // Unknown expr
713                    debug!(
714                        "STACK CFI expression eval failed - unknown token: {}",
715                        token
716                    );
717                    return None;
718                }
719            }
720        }
721    }
722
723    if stack.len() == 1 {
724        stack.pop()
725    } else {
726        None
727    }
728}
729
730#[derive(Debug, Clone, PartialEq, Eq, Hash)]
731enum CfiReg<'a> {
732    Cfa,
733    Ra,
734    Other(&'a str),
735}
736
737#[cfg(feature = "fuzz")]
738pub fn eval_win_expr_for_fuzzer(
739    expr: &str,
740    info: &StackInfoWin,
741    walker: &mut dyn FrameWalker,
742) -> Option<()> {
743    eval_win_expr(expr, info, walker)
744}
745
746fn eval_win_expr(expr: &str, info: &StackInfoWin, walker: &mut dyn FrameWalker) -> Option<()> {
747    // TODO?: do a bunch of heuristics to make this more robust.
748    // So far I haven't encountered an in-the-wild example that needs the
749    // extra heuristics that breakpad uses, so leaving them out until they
750    // become a problem.
751
752    let mut vars = HashMap::new();
753
754    let callee_esp = walker.get_callee_register("esp")? as u32;
755    let callee_ebp = walker.get_callee_register("ebp")? as u32;
756    let grand_callee_param_size = walker.get_grand_callee_parameter_size();
757    let frame_size = win_frame_size(info, grand_callee_param_size);
758
759    // First setup the initial variables
760    vars.insert("$esp", callee_esp);
761    vars.insert("$ebp", callee_ebp);
762    if let Some(callee_ebx) = walker.get_callee_register("ebx") {
763        vars.insert("$ebx", callee_ebx as u32);
764    }
765
766    let search_start = if expr.contains('@') {
767        // The frame has been aligned, so don't trust $esp. Assume $ebp
768        // is valid and that the standard calling convention is used
769        // (so the caller's $ebp was pushed right after the return address,
770        // and now $ebp points to that.)
771        trace!("program used @ operator, using $ebp instead of $esp for return addr");
772        callee_ebp.checked_add(4)?
773    } else {
774        // $esp should be reasonable, get the return address from that
775        callee_esp.checked_add(frame_size)?
776    };
777
778    trace!(
779        "raSearchStart = 0x{:08x} (0x{:08x}, 0x{:08x}, 0x{:08x})",
780        search_start,
781        grand_callee_param_size,
782        info.local_size,
783        info.saved_register_size
784    );
785
786    // Magic names from breakpad
787    vars.insert(".cbParams", info.parameter_size);
788    vars.insert(".cbCalleeParams", grand_callee_param_size);
789    vars.insert(".cbSavedRegs", info.saved_register_size);
790    vars.insert(".cbLocals", info.local_size);
791    vars.insert(".raSearch", search_start);
792    vars.insert(".raSearchStart", search_start);
793
794    // FIXME: this should be an ArrayVec or something..?
795    let mut stack: Vec<WinVal> = Vec::new();
796
797    // hack to fix bug where "= NEXT_TOKEN" is sometimes "=NEXT_TOKEN"
798    // for some windows toolchains.
799    let tokens = expr
800        .split_ascii_whitespace()
801        .flat_map(|x| {
802            if x.starts_with('=') && x.len() > 1 {
803                [Some(&x[0..1]), Some(&x[1..])]
804            } else {
805                [Some(x), None]
806            }
807        }) // get rid of the Array
808        .flatten(); // get rid of the Option::None's
809
810    // Evaluate the expressions
811
812    for token in tokens {
813        match token {
814            // FIXME: not sure what overflow/sign semantics are
815            "+" => {
816                // Add
817                let rhs = stack.pop()?.into_int(&vars)?;
818                let lhs = stack.pop()?.into_int(&vars)?;
819                stack.push(WinVal::Int(lhs.wrapping_add(rhs)));
820            }
821            "-" => {
822                // Subtract
823                let rhs = stack.pop()?.into_int(&vars)?;
824                let lhs = stack.pop()?.into_int(&vars)?;
825                stack.push(WinVal::Int(lhs.wrapping_sub(rhs)));
826            }
827            "*" => {
828                // Multiply
829                let rhs = stack.pop()?.into_int(&vars)?;
830                let lhs = stack.pop()?.into_int(&vars)?;
831                stack.push(WinVal::Int(lhs.wrapping_mul(rhs)));
832            }
833            "/" => {
834                // Divide
835                let rhs = stack.pop()?.into_int(&vars)?;
836                let lhs = stack.pop()?.into_int(&vars)?;
837                if rhs == 0 {
838                    // Div by 0
839                    return None;
840                }
841                stack.push(WinVal::Int(lhs.wrapping_div(rhs)));
842            }
843            "%" => {
844                // Remainder
845                let rhs = stack.pop()?.into_int(&vars)?;
846                let lhs = stack.pop()?.into_int(&vars)?;
847                if rhs == 0 {
848                    // Div by 0
849                    return None;
850                }
851                stack.push(WinVal::Int(lhs.wrapping_rem(rhs)));
852            }
853            "@" => {
854                // Align (truncate)
855                let rhs = stack.pop()?.into_int(&vars)?;
856                let lhs = stack.pop()?.into_int(&vars)?;
857
858                if rhs == 0 || !rhs.is_power_of_two() {
859                    return None;
860                }
861
862                // ~Bit Magic Corner~
863                //
864                // A power of two has only one bit set (e.g. 4 is 0b100), and
865                // subtracting 1 from that gets you all 1's below that bit (e.g. 0b011).
866                // -1 is all 1's.
867                //
868                // So XORing -1 with (power_of_2 - 1) gets you all ones except
869                // for the bits lower than the power of 2. ANDing that value
870                // to a number consequently makes it a multiple of that power
871                // of two (all the bits smaller than the power are cleared).
872                stack.push(WinVal::Int(lhs & (-1i32 as u32 ^ (rhs - 1))));
873            }
874            "=" => {
875                // Assign lhs = rhs
876                let rhs = stack.pop()?;
877                let lhs = stack.pop()?.into_var()?;
878
879                if let WinVal::Undef = rhs {
880                    vars.remove(&lhs);
881                } else {
882                    vars.insert(lhs, rhs.into_int(&vars)?);
883                }
884            }
885            "^" => {
886                // Deref the value
887                let ptr = stack.pop()?.into_int(&vars)?;
888                stack.push(WinVal::Int(
889                    walker.get_register_at_address(ptr as u64)? as u32
890                ));
891            }
892            ".undef" => {
893                // This register is explicitly undefined!
894                stack.push(WinVal::Undef);
895            }
896            _ => {
897                // More complex cases
898                if token == ".undef" {
899                    stack.push(WinVal::Undef);
900                } else if token.starts_with('$') || token.starts_with('.') {
901                    // Push a register
902                    stack.push(WinVal::Var(token));
903                } else if let Ok(value) = i32::from_str(token) {
904                    // Push a constant
905                    // FIXME: We do everything in wrapping arithmetic, so it's fine to squash
906                    // i32's into u32's?
907                    stack.push(WinVal::Int(value as u32));
908                } else {
909                    // Unknown expr
910                    trace!(
911                        "STACK WIN expression eval failed - unknown token: {}",
912                        token
913                    );
914                    return None;
915                }
916            }
917        }
918    }
919
920    let output_regs = ["$eip", "$esp", "$ebp", "$ebx", "$esi", "$edi"];
921    for reg in &output_regs {
922        if let Some(&val) = vars.get(reg) {
923            walker.set_caller_register(&reg[1..], val as u64)?;
924        }
925    }
926
927    trace!("STACK WIN expression eval succeeded!");
928
929    Some(())
930}
931
932fn win_frame_size(info: &StackInfoWin, grand_callee_param_size: u32) -> u32 {
933    info.local_size + info.saved_register_size + grand_callee_param_size
934}
935
936enum WinVal<'a> {
937    Var(&'a str),
938    Int(u32),
939    Undef,
940}
941
942impl<'a> WinVal<'a> {
943    fn into_var(self) -> Option<&'a str> {
944        if let WinVal::Var(var) = self {
945            Some(var)
946        } else {
947            None
948        }
949    }
950    fn into_int(self, map: &HashMap<&'a str, u32>) -> Option<u32> {
951        match self {
952            WinVal::Var(var) => map.get(&var).cloned(),
953            WinVal::Int(int) => Some(int),
954            WinVal::Undef => None,
955        }
956    }
957}
958
959pub fn walk_with_stack_win_framedata(
960    info: &StackInfoWin,
961    walker: &mut dyn FrameWalker,
962) -> Option<()> {
963    if let WinStackThing::ProgramString(ref expr) = info.program_string_or_base_pointer {
964        trace!("trying STACK WIN framedata -- {}", expr);
965        clear_stack_win_caller_registers(walker);
966        eval_win_expr(expr, info, walker)
967    } else {
968        unreachable!()
969    }
970}
971
972pub fn walk_with_stack_win_fpo(info: &StackInfoWin, walker: &mut dyn FrameWalker) -> Option<()> {
973    if let WinStackThing::AllocatesBasePointer(allocates_base_pointer) =
974        info.program_string_or_base_pointer
975    {
976        // FIXME: do a bunch of heuristics to make this more robust.
977        // Haven't needed the heuristics breakpad uses yet.
978        trace!("trying STACK WIN fpo");
979        clear_stack_win_caller_registers(walker);
980
981        let grand_callee_param_size = walker.get_grand_callee_parameter_size();
982        let frame_size = win_frame_size(info, grand_callee_param_size) as u64;
983
984        let callee_esp = walker.get_callee_register("esp")?;
985        let mut eip_address = callee_esp + frame_size;
986        let mut caller_eip = walker.get_register_at_address(eip_address)?;
987
988        // Check for a "leftover return address": in some pathological cases the return address isn't popped off the stack
989        // after a return instruction. According to breakpad, this can happen for "frame-pointer-optimized
990        // system calls", which implies that the callee must be a context frame.
991        //
992        // To detect these cases, we check whether
993        // 1. we are in a context frame. We approximate this by checking whether there's a grand-callee.
994        // 2. the caller's eip (aka the return address) is the same as the callee's eip.
995        //
996        // If we detect a leftover return address, we skip it and try again one word
997        // further down the stack.
998        let callee_is_context_frame = !walker.has_grand_callee();
999        if callee_is_context_frame && caller_eip == walker.get_callee_register("eip")? {
1000            eip_address += 4;
1001            caller_eip = walker.get_register_at_address(eip_address)?;
1002        }
1003        let caller_esp = eip_address + 4;
1004
1005        trace!("found caller $eip and $esp");
1006
1007        let caller_ebp = if allocates_base_pointer {
1008            let ebp_address =
1009                callee_esp + grand_callee_param_size as u64 + info.saved_register_size as u64 - 8;
1010            walker.get_register_at_address(ebp_address)?
1011        } else {
1012            // Per Breakpad: We also propagate %ebx through, as it is commonly unmodifed after
1013            // calling simple forwarding functions in ntdll (that are this non-EBP
1014            // using type). It's not clear that this is always correct, but it is
1015            // important for some functions to get a correct walk.
1016            if let Some(callee_ebx) = walker.get_callee_register("ebx") {
1017                walker.set_caller_register("ebx", callee_ebx)?;
1018            }
1019
1020            walker.get_callee_register("ebp")?
1021        };
1022        trace!("found caller $ebp");
1023
1024        walker.set_caller_register("eip", caller_eip)?;
1025        walker.set_caller_register("esp", caller_esp)?;
1026        walker.set_caller_register("ebp", caller_ebp)?;
1027
1028        trace!("STACK WIN fpo eval succeeded!");
1029        Some(())
1030    } else {
1031        unreachable!()
1032    }
1033}
1034
1035/// STACK WIN doesn't want implicit register forwarding
1036fn clear_stack_win_caller_registers(walker: &mut dyn FrameWalker) {
1037    let output_regs = ["$eip", "$esp", "$ebp", "$ebx", "$esi", "$edi"];
1038    for reg in output_regs {
1039        walker.clear_caller_register(reg);
1040    }
1041}
1042
1043#[cfg(test)]
1044mod test {
1045    use super::super::types::{CfiRules, StackInfoWin, WinStackThing};
1046    use super::{eval_win_expr, walk_with_stack_cfi, walk_with_stack_win_fpo};
1047    use crate::FrameWalker;
1048    use std::collections::HashMap;
1049
1050    // Eugh, need this to memoize register names to static
1051    static STATIC_REGS: [&str; 14] = [
1052        "cfa", "ra", "esp", "eip", "ebp", "eax", "ebx", "rsp", "rip", "rbp", "rax", "rbx", "x11",
1053        "x12",
1054    ];
1055
1056    struct TestFrameWalker<Reg> {
1057        instruction: Reg,
1058        has_grand_callee: bool,
1059        grand_callee_param_size: u32,
1060        callee_regs: HashMap<&'static str, Reg>,
1061        caller_regs: HashMap<&'static str, Reg>,
1062        stack: Vec<u8>,
1063    }
1064
1065    trait Int {
1066        const BYTES: usize;
1067        fn from_bytes(bytes: &[u8]) -> Self;
1068        fn into_u64(self) -> u64;
1069        fn from_u64(val: u64) -> Self;
1070    }
1071    impl Int for u32 {
1072        const BYTES: usize = 4;
1073        fn from_bytes(bytes: &[u8]) -> Self {
1074            let mut buf = [0; Self::BYTES];
1075            buf.copy_from_slice(bytes);
1076            u32::from_le_bytes(buf)
1077        }
1078        fn into_u64(self) -> u64 {
1079            self as u64
1080        }
1081        fn from_u64(val: u64) -> Self {
1082            val as u32
1083        }
1084    }
1085    impl Int for u64 {
1086        const BYTES: usize = 8;
1087        fn from_bytes(bytes: &[u8]) -> Self {
1088            let mut buf = [0; Self::BYTES];
1089            buf.copy_from_slice(bytes);
1090            u64::from_le_bytes(buf)
1091        }
1092        fn into_u64(self) -> u64 {
1093            self
1094        }
1095        fn from_u64(val: u64) -> Self {
1096            val
1097        }
1098    }
1099
1100    impl<Reg: Int + Copy> FrameWalker for TestFrameWalker<Reg> {
1101        fn get_instruction(&self) -> u64 {
1102            self.instruction.into_u64()
1103        }
1104        fn has_grand_callee(&self) -> bool {
1105            self.has_grand_callee
1106        }
1107        fn get_grand_callee_parameter_size(&self) -> u32 {
1108            self.grand_callee_param_size
1109        }
1110        /// Get a register-sized value stored at this address.
1111        fn get_register_at_address(&self, address: u64) -> Option<u64> {
1112            let addr = address as usize;
1113            self.stack
1114                .get(addr..addr + Reg::BYTES)
1115                .map(|slice| Reg::from_bytes(slice).into_u64())
1116        }
1117        /// Get the value of a register from the callee's frame.
1118        fn get_callee_register(&self, name: &str) -> Option<u64> {
1119            self.callee_regs.get(name).map(|val| val.into_u64())
1120        }
1121        /// Set the value of a register for the caller's frame.
1122        fn set_caller_register(&mut self, name: &str, val: u64) -> Option<()> {
1123            STATIC_REGS.iter().position(|&reg| reg == name).map(|idx| {
1124                let memoized_reg = STATIC_REGS[idx];
1125                self.caller_regs.insert(memoized_reg, Reg::from_u64(val));
1126            })
1127        }
1128        fn clear_caller_register(&mut self, name: &str) {
1129            self.caller_regs.remove(name);
1130        }
1131        /// Set whatever registers in the caller should be set based on the cfa (e.g. rsp).
1132        fn set_cfa(&mut self, val: u64) -> Option<()> {
1133            self.caller_regs.insert("cfa", Reg::from_u64(val));
1134            Some(())
1135        }
1136        /// Set whatever registers in the caller should be set based on the return address (e.g. rip).
1137        fn set_ra(&mut self, val: u64) -> Option<()> {
1138            self.caller_regs.insert("ra", Reg::from_u64(val));
1139            Some(())
1140        }
1141    }
1142
1143    impl<Reg: Int + Copy> TestFrameWalker<Reg> {
1144        fn new(stack: Vec<u8>, callee_regs: HashMap<&'static str, Reg>) -> Self {
1145            TestFrameWalker {
1146                stack,
1147                callee_regs,
1148                caller_regs: HashMap::new(),
1149
1150                // Arbitrary values
1151                instruction: Reg::from_u64(0xF1CEFA32),
1152                has_grand_callee: true,
1153                grand_callee_param_size: 4,
1154            }
1155        }
1156    }
1157
1158    /// Arbitrary default values in case needed.
1159    fn whatever_win_info() -> StackInfoWin {
1160        StackInfoWin {
1161            address: 0xFEA4A123,
1162            size: 16,
1163            prologue_size: 4,
1164            epilogue_size: 8,
1165            parameter_size: 16,
1166            saved_register_size: 12,
1167            local_size: 24,
1168            max_stack_size: 64,
1169            program_string_or_base_pointer: WinStackThing::AllocatesBasePointer(false),
1170        }
1171    }
1172
1173    fn build_cfi_rules(init: &str, additional: &[&str]) -> (CfiRules, Vec<CfiRules>) {
1174        let init = CfiRules {
1175            address: 0,
1176            rules: init.to_string(),
1177        };
1178        let additional = additional
1179            .iter()
1180            .enumerate()
1181            .map(|(idx, rules)| CfiRules {
1182                address: idx as u64 + 1,
1183                rules: rules.to_string(),
1184            })
1185            .collect::<Vec<_>>();
1186
1187        (init, additional)
1188    }
1189
1190    #[test]
1191    fn test_stack_win_doc_example() {
1192        // Final output of `ebp=(*16)`, `esp=24`, `eip=(*20)`.
1193        let expr = "$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =";
1194        let input = vec![("ebp", 16u32), ("esp", 1600)].into_iter().collect();
1195        let mut stack = vec![0; 1600];
1196
1197        const FINAL_EBP: u32 = 0xFA1EF2E6;
1198        const FINAL_EIP: u32 = 0xB3EF04CE;
1199
1200        stack[16..20].copy_from_slice(&FINAL_EBP.to_le_bytes());
1201        stack[20..24].copy_from_slice(&FINAL_EIP.to_le_bytes());
1202
1203        let mut walker = TestFrameWalker::new(stack, input);
1204        let info = whatever_win_info();
1205
1206        eval_win_expr(expr, &info, &mut walker).unwrap();
1207
1208        assert_eq!(walker.caller_regs.len(), 3);
1209        assert_eq!(walker.caller_regs["esp"], 24);
1210        assert_eq!(walker.caller_regs["ebp"], FINAL_EBP);
1211        assert_eq!(walker.caller_regs["eip"], FINAL_EIP);
1212    }
1213
1214    #[test]
1215    fn test_stack_win_ops() {
1216        // Making sure all the operators do what they should.
1217        let input = vec![("esp", 32u32), ("ebp", 1600)].into_iter().collect();
1218        let stack = vec![0; 1600];
1219
1220        let mut walker = TestFrameWalker::new(stack, input);
1221        let info = whatever_win_info();
1222
1223        // Addition!
1224        walker.caller_regs.clear();
1225        eval_win_expr("$esp 1 2 + = $ebp -4 0 + =", &info, &mut walker).unwrap();
1226
1227        assert_eq!(walker.caller_regs.len(), 2);
1228        assert_eq!(walker.caller_regs["esp"], 3);
1229        assert_eq!(walker.caller_regs["ebp"], -4i32 as u32);
1230
1231        // Subtraction!
1232        walker.caller_regs.clear();
1233        eval_win_expr("$esp 5 3 - = $ebp -4 2 - =", &info, &mut walker).unwrap();
1234
1235        assert_eq!(walker.caller_regs.len(), 2);
1236        assert_eq!(walker.caller_regs["esp"], 2);
1237        assert_eq!(walker.caller_regs["ebp"], -6i32 as u32);
1238
1239        // Multiplication!
1240        walker.caller_regs.clear();
1241        eval_win_expr("$esp 5 3 * = $ebp -4 2 * =", &info, &mut walker).unwrap();
1242
1243        assert_eq!(walker.caller_regs.len(), 2);
1244        assert_eq!(walker.caller_regs["esp"], 15);
1245        assert_eq!(walker.caller_regs["ebp"], -8i32 as u32);
1246
1247        // Division!
1248        walker.caller_regs.clear();
1249        eval_win_expr("$esp 5 3 / = $ebp -4 2 / =", &info, &mut walker).unwrap();
1250
1251        assert_eq!(walker.caller_regs.len(), 2);
1252        assert_eq!(walker.caller_regs["esp"], 1);
1253        // TODO: oh no this fails, u64/u32 mismatches ARE a problem... at least
1254        // for this synthetic example!
1255        // assert_eq!(walker.caller_regs["ebp"], -2i32 as u32);
1256
1257        // Modulo!
1258        walker.caller_regs.clear();
1259        eval_win_expr("$esp  5 3 %  = $ebp -1 2 % = ", &info, &mut walker).unwrap();
1260
1261        assert_eq!(walker.caller_regs.len(), 2);
1262        assert_eq!(walker.caller_regs["esp"], 2);
1263        assert_eq!(walker.caller_regs["ebp"], 1);
1264
1265        // Align!
1266        walker.caller_regs.clear();
1267        eval_win_expr("$esp  8 16 @ = $ebp 161 8 @ = ", &info, &mut walker).unwrap();
1268
1269        assert_eq!(walker.caller_regs.len(), 2);
1270        assert_eq!(walker.caller_regs["esp"], 0);
1271        assert_eq!(walker.caller_regs["ebp"], 160);
1272
1273        // Operator Errors - Missing Inputs
1274
1275        // + missing args
1276        assert!(eval_win_expr("1 + ", &info, &mut walker).is_none());
1277
1278        // - missing args
1279        assert!(eval_win_expr("1 -", &info, &mut walker).is_none());
1280
1281        // * missing args
1282        assert!(eval_win_expr("1 *", &info, &mut walker).is_none());
1283
1284        // / missing args
1285        assert!(eval_win_expr("1 /", &info, &mut walker).is_none());
1286
1287        // % missing args
1288        assert!(eval_win_expr("1 %", &info, &mut walker).is_none());
1289
1290        // @ missing args
1291        assert!(eval_win_expr("1 @", &info, &mut walker).is_none());
1292
1293        // ^ missing arg
1294        assert!(eval_win_expr("^", &info, &mut walker).is_none());
1295
1296        // Operator Errors - Invalid Inputs
1297
1298        // / by 0
1299        assert!(eval_win_expr("$esp 1 0 / = $ebp 1 =", &info, &mut walker).is_none());
1300
1301        // % by 0
1302        assert!(eval_win_expr("$esp 1 0 % = $ebp 1 =", &info, &mut walker).is_none());
1303
1304        // @ by 0
1305        assert!(eval_win_expr("$esp 1 0 @ = $ebp 1 =", &info, &mut walker).is_none());
1306
1307        // @ not power of 2
1308        assert!(eval_win_expr("$esp 1 3 @ = $ebp 1 =", &info, &mut walker).is_none());
1309    }
1310
1311    #[test]
1312    fn test_stack_win_corners() {
1313        // Making sure all the operators do what they should.
1314        let input = vec![("esp", 32u32), ("ebp", 1600)].into_iter().collect();
1315        let stack = vec![0; 1600];
1316
1317        let mut walker = TestFrameWalker::new(stack, input);
1318        let info = whatever_win_info();
1319
1320        // Empty expression is ok, just forward through registers
1321        walker.caller_regs.clear();
1322        eval_win_expr("", &info, &mut walker).unwrap();
1323
1324        assert_eq!(walker.caller_regs.len(), 2);
1325        assert_eq!(walker.caller_regs["esp"], 32);
1326        assert_eq!(walker.caller_regs["ebp"], 1600);
1327
1328        // Undef works
1329        walker.caller_regs.clear();
1330        eval_win_expr("$esp .undef = $ebp .undef =", &info, &mut walker).unwrap();
1331
1332        assert_eq!(walker.caller_regs.len(), 0);
1333
1334        // Idempotent works
1335        walker.caller_regs.clear();
1336        eval_win_expr("$esp $esp = $ebp $ebp =", &info, &mut walker).unwrap();
1337
1338        assert_eq!(walker.caller_regs.len(), 2);
1339        assert_eq!(walker.caller_regs["esp"], 32);
1340        assert_eq!(walker.caller_regs["ebp"], 1600);
1341
1342        // Trailing garbage in the stack is ok
1343        walker.caller_regs.clear();
1344        eval_win_expr("$esp 1 = $ebp 2 = 3 4 5", &info, &mut walker).unwrap();
1345
1346        assert_eq!(walker.caller_regs.len(), 2);
1347        assert_eq!(walker.caller_regs["esp"], 1);
1348        assert_eq!(walker.caller_regs["ebp"], 2);
1349
1350        // Trailing garbage in the stack is ok (with variables)
1351        walker.caller_regs.clear();
1352        eval_win_expr("$esp 1 = $ebp 2 = 3 4 5 $esp $eax", &info, &mut walker).unwrap();
1353
1354        assert_eq!(walker.caller_regs.len(), 2);
1355        assert_eq!(walker.caller_regs["esp"], 1);
1356        assert_eq!(walker.caller_regs["ebp"], 2);
1357
1358        // Temporaries don't get assigned to output
1359        walker.caller_regs.clear();
1360        eval_win_expr("$t0 1 = $esp $t0 5 + = $ebp 2 =", &info, &mut walker).unwrap();
1361
1362        assert_eq!(walker.caller_regs.len(), 2);
1363        assert_eq!(walker.caller_regs["esp"], 6);
1364        assert_eq!(walker.caller_regs["ebp"], 2);
1365
1366        // Variables can be assigned after they are pushed
1367        walker.caller_regs.clear();
1368        eval_win_expr("$esp  $T0 $T0 2 = = $ebp 3 =", &info, &mut walker).unwrap();
1369
1370        assert_eq!(walker.caller_regs.len(), 2);
1371        assert_eq!(walker.caller_regs["esp"], 2);
1372        assert_eq!(walker.caller_regs["ebp"], 3);
1373    }
1374
1375    #[test]
1376    fn test_stack_win_errors() {
1377        // Making sure all the operators do what they should.
1378        let input = vec![("esp", 32u32), ("ebp", 1600)].into_iter().collect();
1379        let stack = vec![0; 1600];
1380
1381        let mut walker = TestFrameWalker::new(stack, input);
1382        let info = whatever_win_info();
1383
1384        // Deref out of bounds
1385        assert!(eval_win_expr("$esp 2000 ^ =", &info, &mut walker).is_none());
1386
1387        // Reading undefined value
1388        assert!(eval_win_expr("$esp $kitties =", &info, &mut walker).is_none());
1389
1390        // Reading value before defined
1391        assert!(eval_win_expr("$esp $kitties = $kitties 1 =", &info, &mut walker).is_none());
1392
1393        // Reading deleted value
1394        assert!(eval_win_expr("$esp .undef = $ebp $esp =", &info, &mut walker).is_none());
1395
1396        // Assigning value to value
1397        assert!(eval_win_expr("0 2 =", &info, &mut walker).is_none());
1398
1399        // Assigning variable to value
1400        assert!(eval_win_expr("0 $esp =", &info, &mut walker).is_none());
1401
1402        // Variables must start with $ or .
1403        assert!(eval_win_expr("esp 2 = ebp 3 =", &info, &mut walker).is_none());
1404    }
1405
1406    #[test]
1407    fn test_stack_win_equal_fixup() {
1408        // Bug in old windows toolchains that sometimes cause = to lose
1409        // its trailing space. Although we would ideally reject this, we're
1410        // at the mercy of what toolchains emit :(
1411
1412        // TODO: this test currently fails! (hence the #[ignore])
1413
1414        let input = vec![("esp", 32u32), ("ebp", 1600)].into_iter().collect();
1415        let stack = vec![0; 1600];
1416
1417        let mut walker = TestFrameWalker::new(stack, input);
1418        let info = whatever_win_info();
1419
1420        eval_win_expr("$esp 1 =$ebp 2 =", &info, &mut walker).unwrap();
1421        assert_eq!(walker.caller_regs.len(), 2);
1422        assert_eq!(walker.caller_regs["esp"], 1);
1423        assert_eq!(walker.caller_regs["ebp"], 2);
1424    }
1425
1426    #[test]
1427    #[ignore]
1428    fn test_stack_win_negative_division() {
1429        // Negative division issues
1430
1431        // TODO: this test currently fails! (hence the #[ignore])
1432
1433        let input = vec![("esp", 32u32), ("ebp", 1600)].into_iter().collect();
1434        let stack = vec![0; 1600];
1435
1436        let mut walker = TestFrameWalker::new(stack, input);
1437        let info = whatever_win_info();
1438
1439        // Division!
1440        walker.caller_regs.clear();
1441        eval_win_expr("$esp 5 3 / = $ebp -4 2 / =", &info, &mut walker).unwrap();
1442
1443        assert_eq!(walker.caller_regs.len(), 2);
1444        assert_eq!(walker.caller_regs["esp"], 1);
1445        assert_eq!(walker.caller_regs["ebp"], -2i32 as u32);
1446    }
1447
1448    #[test]
1449    fn test_stack_win_leftover_return_address() {
1450        // The return address on top of the stack (0xABCD_1234) is equal to the callee's eip, indicating
1451        // a return address that was left over from a return. The stackwalker should skip it and
1452        // return the second value on the stack (0xABCD_5678) as the caller's eip.
1453        let stack = vec![0x34, 0x12, 0xCD, 0xAB, 0x78, 0x56, 0xCD, 0xAB];
1454        let mut walker = TestFrameWalker {
1455            instruction: 0xABCD_1234u32,
1456            has_grand_callee: false,
1457            grand_callee_param_size: 0,
1458            callee_regs: vec![("eip", 0xABCD_1234), ("esp", 0), ("ebp", 17)]
1459                .into_iter()
1460                .collect(),
1461            caller_regs: HashMap::new(),
1462            stack,
1463        };
1464
1465        // these are all dummy values
1466        let info = StackInfoWin {
1467            address: 0,
1468            size: 0,
1469            prologue_size: 0,
1470            epilogue_size: 0,
1471            parameter_size: 0,
1472            saved_register_size: 0,
1473            local_size: 0,
1474            max_stack_size: 0,
1475            program_string_or_base_pointer: WinStackThing::AllocatesBasePointer(false),
1476        };
1477
1478        walk_with_stack_win_fpo(&info, &mut walker).unwrap();
1479
1480        assert_eq!(walker.caller_regs["esp"], 8);
1481        assert_eq!(walker.caller_regs["ebp"], 17);
1482        assert_eq!(walker.caller_regs["eip"], 0xABCD_5678);
1483    }
1484
1485    #[test]
1486    fn test_stack_cfi_doc_example() {
1487        // Final output of:
1488        //
1489        // cfa = callee_rsp + 24
1490        // ra = *(cfa - 8)
1491        // rax = *(cfa - 16)
1492
1493        let init = ".cfa: $rsp 8 + .ra: .cfa -8 + ^";
1494        let additional = &[".cfa: $rsp 16 + $rax: .cfa -16 + ^", ".cfa: $rsp 24 +"];
1495        let input = vec![("rsp", 32u64), ("rip", 1600)].into_iter().collect();
1496        let mut stack = vec![0; 1600];
1497
1498        const FINAL_CFA: usize = 32 + 24;
1499        const FINAL_RA: u64 = 0xFA1E_F2E6_A2DF_2B68;
1500        const FINAL_RAX: u64 = 0xB3EF_04CE_4321_FE2A;
1501
1502        stack[FINAL_CFA - 8..FINAL_CFA].copy_from_slice(&FINAL_RA.to_le_bytes());
1503        stack[FINAL_CFA - 16..FINAL_CFA - 8].copy_from_slice(&FINAL_RAX.to_le_bytes());
1504
1505        let mut walker = TestFrameWalker::new(stack, input);
1506        let (init, additional) = build_cfi_rules(init, additional);
1507        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1508
1509        assert_eq!(walker.caller_regs.len(), 3);
1510        assert_eq!(walker.caller_regs["cfa"], FINAL_CFA as u64);
1511        assert_eq!(walker.caller_regs["ra"], FINAL_RA);
1512        assert_eq!(walker.caller_regs["rax"], FINAL_RAX);
1513    }
1514
1515    #[test]
1516    fn test_stack_cfi_ops() {
1517        // Making sure all the operators do what they should, using 32-bit
1518        // to stress truncation issues from u64 <-> u32 mapping of the
1519        // abstraction.
1520        let input = vec![("esp", 32u32), ("eip", 1600)].into_iter().collect();
1521        let stack = vec![0; 1600];
1522
1523        let mut walker = TestFrameWalker::new(stack, input);
1524
1525        // Addition!
1526        walker.caller_regs.clear();
1527        let (init, additional) = build_cfi_rules(".cfa: 1 2 + .ra: -4 0 +", &[]);
1528        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1529
1530        assert_eq!(walker.caller_regs.len(), 2);
1531        assert_eq!(walker.caller_regs["cfa"], 3);
1532        assert_eq!(walker.caller_regs["ra"], -4i32 as u32);
1533
1534        // Subtraction!
1535        walker.caller_regs.clear();
1536        let (init, additional) = build_cfi_rules(".cfa: 5 3 - .ra: -4 2 -", &[]);
1537        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1538
1539        assert_eq!(walker.caller_regs.len(), 2);
1540        assert_eq!(walker.caller_regs["cfa"], 2);
1541        assert_eq!(walker.caller_regs["ra"], -6i32 as u32);
1542
1543        // Multiplication!
1544        walker.caller_regs.clear();
1545        let (init, additional) = build_cfi_rules(".cfa: 5 3 * .ra: -4 2 *", &[]);
1546        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1547
1548        assert_eq!(walker.caller_regs.len(), 2);
1549        assert_eq!(walker.caller_regs["cfa"], 15);
1550        assert_eq!(walker.caller_regs["ra"], -8i32 as u32);
1551
1552        // Division!
1553        walker.caller_regs.clear();
1554        let (init, additional) = build_cfi_rules(".cfa: 5 3 / .ra: -4 2 /", &[]);
1555        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1556
1557        assert_eq!(walker.caller_regs.len(), 2);
1558        assert_eq!(walker.caller_regs["cfa"], 1);
1559        assert_eq!(walker.caller_regs["ra"], -2i32 as u32);
1560
1561        // Modulo!
1562        walker.caller_regs.clear();
1563        let (init, additional) = build_cfi_rules(".cfa: 5 3 % .ra: -1 2 %", &[]);
1564        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1565
1566        assert_eq!(walker.caller_regs.len(), 2);
1567        assert_eq!(walker.caller_regs["cfa"], 2);
1568        assert_eq!(walker.caller_regs["ra"], 1);
1569
1570        // Align!
1571        walker.caller_regs.clear();
1572        let (init, additional) = build_cfi_rules(".cfa: 8 16 @ .ra: 161 8 @", &[]);
1573        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1574
1575        assert_eq!(walker.caller_regs.len(), 2);
1576        assert_eq!(walker.caller_regs["cfa"], 0);
1577        assert_eq!(walker.caller_regs["ra"], 160);
1578
1579        // Operator Errors - Missing Inputs
1580
1581        // + missing args
1582        let (init, additional) = build_cfi_rules(".cfa: 1 + .ra: 8", &[]);
1583        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1584
1585        // - missing args
1586        let (init, additional) = build_cfi_rules(".cfa: 1 - .ra: 8", &[]);
1587        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1588
1589        // * missing args
1590        let (init, additional) = build_cfi_rules(".cfa: 1 * .ra: 8", &[]);
1591        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1592
1593        // / missing args
1594        let (init, additional) = build_cfi_rules(".cfa: 1 / .ra: 8", &[]);
1595        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1596
1597        // % missing args
1598        let (init, additional) = build_cfi_rules(".cfa: 1 % .ra: 8", &[]);
1599        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1600
1601        // @ missing args
1602        let (init, additional) = build_cfi_rules(".cfa: 1 @ .ra: 8", &[]);
1603        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1604
1605        // ^ missing arg
1606        let (init, additional) = build_cfi_rules(".cfa: ^ .ra: 8", &[]);
1607        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1608
1609        // Operator Errors - Invalid Inputs
1610
1611        // / by 0
1612        let (init, additional) = build_cfi_rules(".cfa: 1 0 / .ra: 8", &[]);
1613        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1614
1615        // % by 0
1616        let (init, additional) = build_cfi_rules(".cfa: 1 0 % .ra: 8", &[]);
1617        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1618
1619        // @ by 0
1620        let (init, additional) = build_cfi_rules(".cfa: 1 0 @ .ra: 8", &[]);
1621        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1622
1623        // @ not power of 2
1624        let (init, additional) = build_cfi_rules(".cfa: 1 3 @ .ra: 8", &[]);
1625        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1626    }
1627
1628    #[test]
1629    fn test_stack_cfi_errors() {
1630        // Checking various issues that we should bail on
1631        let input = vec![("rsp", 32u64), ("rip", 1600)].into_iter().collect();
1632        let stack = vec![0; 1600];
1633
1634        let mut walker = TestFrameWalker::new(stack, input);
1635
1636        // Basic syntax
1637
1638        // Missing .ra
1639        let (init, additional) = build_cfi_rules(".cfa: 8 16 +", &[]);
1640        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1641
1642        // Missing .cfa
1643        let (init, additional) = build_cfi_rules(".ra: 8 16 *", &[]);
1644        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1645
1646        // No : at all
1647        let (init, additional) = build_cfi_rules(".cfa 8 16 *", &[]);
1648        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1649
1650        // Doesn't start with a REG
1651        let (init, additional) = build_cfi_rules(".esp 8 16 * .cfa: 16 .ra: 8", &[]);
1652        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1653
1654        // .cfa has extra junk on stack
1655        let (init, additional) = build_cfi_rules(".cfa: 8 12 .ra: 8", &[]);
1656        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1657
1658        // REG has empty expr (trailing)
1659        let (init, additional) = build_cfi_rules(".cfa: 12 .ra: 8 $rax:", &[]);
1660        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1661
1662        // REG has empty expr (trailing with space)
1663        let (init, additional) = build_cfi_rules(".cfa: 12 .ra: 8 $rax: ", &[]);
1664        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1665
1666        // REG has empty expr (middle)
1667        let (init, additional) = build_cfi_rules(".cfa: 12 .ra: 8 $rax: $rbx: 8", &[]);
1668        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1669
1670        // Make sure = operator isn't supported in this implementation
1671        let (init, additional) = build_cfi_rules(".cfa: 12 .ra: $rsp $rip =", &[]);
1672        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1673
1674        // .cfa is undef
1675        let (init, additional) = build_cfi_rules(".cfa: .undef .ra: 8", &[]);
1676        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1677
1678        // .ra is undef
1679        let (init, additional) = build_cfi_rules(".cfa: 8 .ra: .undef", &[]);
1680        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1681
1682        // Reading out of bounds
1683        let (init, additional) = build_cfi_rules(".cfa: 2000 ^ .ra: 8", &[]);
1684        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1685
1686        // Reading fake $reg
1687        let (init, additional) = build_cfi_rules(".cfa: 8 .ra: $kitties", &[]);
1688        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1689
1690        // Reading real but still undefined $reg
1691        let (init, additional) = build_cfi_rules(".cfa: 8 .ra: $rax", &[]);
1692        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1693
1694        // Reading .cfa for .cfa's own value
1695        let (init, additional) = build_cfi_rules(".cfa: .cfa .ra: 2", &[]);
1696        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1697
1698        // Reading .ra for .cfa's value
1699        let (init, additional) = build_cfi_rules(".cfa: .ra .ra: 2", &[]);
1700        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1701
1702        // Reading .ra for .ra's value
1703        let (init, additional) = build_cfi_rules(".cfa: 1 .ra: .ra", &[]);
1704        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1705
1706        // Malformed doc example shouldn't work (found while typoing docs)
1707        // Note the first .cfa in the additional lines has no `:`!
1708        let (init, additional) = build_cfi_rules(
1709            ".cfa: $rsp 8 + .ra: .cfa -8 + ^",
1710            &[".cfa $rsp 16 + $rax: .cfa -16 + ^", ".cfa $rsp 24 +"],
1711        );
1712        assert!(walk_with_stack_cfi(&init, &additional, &mut walker).is_none());
1713    }
1714
1715    #[test]
1716    fn test_stack_cfi_corners() {
1717        // Checking various issues that we should bail on
1718        let input = vec![("rsp", 32u64), ("rip", 1600)].into_iter().collect();
1719        let stack = vec![0; 1600];
1720
1721        let mut walker = TestFrameWalker::new(stack, input);
1722
1723        // Just a value for each reg (no ops to execute)
1724        walker.caller_regs.clear();
1725        let (init, additional) = build_cfi_rules(".cfa: 8 .ra: 12 $rax: 16", &[]);
1726        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1727
1728        assert_eq!(walker.caller_regs.len(), 3);
1729        assert_eq!(walker.caller_regs["cfa"], 8);
1730        assert_eq!(walker.caller_regs["ra"], 12);
1731        assert_eq!(walker.caller_regs["rax"], 16);
1732
1733        // Undef $REGs are ok, Undef in the middle of expr ok
1734        walker.caller_regs.clear();
1735        let (init, additional) =
1736            build_cfi_rules(".cfa: 8 .ra: 12 $rax: .undef $rbx: 1 .undef +", &[]);
1737        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1738
1739        assert_eq!(walker.caller_regs.len(), 2);
1740        assert_eq!(walker.caller_regs["cfa"], 8);
1741        assert_eq!(walker.caller_regs["ra"], 12);
1742
1743        // Unknown $reg output is ok; evaluated but value discarded
1744        walker.caller_regs.clear();
1745        let (init, additional) = build_cfi_rules(".cfa: 8 .ra: 12 $kitties: 16", &[]);
1746        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1747
1748        assert_eq!(walker.caller_regs.len(), 2);
1749        assert_eq!(walker.caller_regs["cfa"], 8);
1750        assert_eq!(walker.caller_regs["ra"], 12);
1751
1752        // Smooshed regs are garbage but we don't validate the string so it should work
1753        // the same as an unknown reg (dubious behaviour but hey let's be aware of it).
1754        walker.caller_regs.clear();
1755        let (init, additional) = build_cfi_rules(".cfa: 12 .ra: 8 $rax:$rbx: 8", &[]);
1756        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1757
1758        assert_eq!(walker.caller_regs.len(), 2);
1759        assert_eq!(walker.caller_regs["cfa"], 12);
1760        assert_eq!(walker.caller_regs["ra"], 8);
1761
1762        // Evaluation errors for $reg output ok; value is discarded
1763        walker.caller_regs.clear();
1764        let (init, additional) = build_cfi_rules(".cfa: 1 .ra: 8 $rax: 1 0 /", &[]);
1765        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1766
1767        assert_eq!(walker.caller_regs.len(), 2);
1768        assert_eq!(walker.caller_regs["cfa"], 1);
1769        assert_eq!(walker.caller_regs["ra"], 8);
1770
1771        // Duplicate records are ok (use the later one)
1772        walker.caller_regs.clear();
1773        let (init, additional) =
1774            build_cfi_rules(".cfa: 1 .cfa: 2 .ra: 3 .ra: 4 $rax: 5 $rax: 6", &[]);
1775        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1776
1777        assert_eq!(walker.caller_regs.len(), 3);
1778        assert_eq!(walker.caller_regs["cfa"], 2);
1779        assert_eq!(walker.caller_regs["ra"], 4);
1780        assert_eq!(walker.caller_regs["rax"], 6);
1781
1782        // Using .cfa works fine
1783        walker.caller_regs.clear();
1784        let (init, additional) = build_cfi_rules(".cfa: 7 .ra: .cfa 1 + $rax: .cfa 2 -", &[]);
1785        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1786
1787        assert_eq!(walker.caller_regs.len(), 3);
1788        assert_eq!(walker.caller_regs["cfa"], 7);
1789        assert_eq!(walker.caller_regs["ra"], 8);
1790        assert_eq!(walker.caller_regs["rax"], 5);
1791
1792        // Reading .ra for $REG's value is ok; value is discarded
1793        walker.caller_regs.clear();
1794        let (init, additional) = build_cfi_rules(".cfa: 1 .ra: 2 $rax: .ra", &[]);
1795        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1796
1797        assert_eq!(walker.caller_regs.len(), 2);
1798        assert_eq!(walker.caller_regs["cfa"], 1);
1799        assert_eq!(walker.caller_regs["ra"], 2);
1800
1801        // Undefined destination .reg is assumed to be an ARM-style register, is dropped
1802        let (init, additional) = build_cfi_rules(".cfa: 8 .ra: 12 .kitties: 16", &[]);
1803        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1804        assert_eq!(walker.caller_regs.len(), 2);
1805        assert_eq!(walker.caller_regs["cfa"], 8);
1806        assert_eq!(walker.caller_regs["ra"], 12);
1807
1808        // Trying to write to .undef is assumed to be an ARM-style register, is dropped
1809        let (init, additional) = build_cfi_rules(".cfa: 8 .ra: 12 .undef: 16", &[]);
1810        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1811        assert_eq!(walker.caller_regs.len(), 2);
1812        assert_eq!(walker.caller_regs["cfa"], 8);
1813        assert_eq!(walker.caller_regs["ra"], 12);
1814    }
1815
1816    #[test]
1817    fn test_stack_cfi_arm() {
1818        // ARM doesn't prefix registers with $
1819        // Checking various issues that we should bail on
1820        let input = vec![("pc", 32u64), ("x11", 1600)].into_iter().collect();
1821        let stack = vec![0; 1600];
1822
1823        let mut walker = TestFrameWalker::new(stack, input);
1824
1825        // Just a value for each reg (no ops to execute)
1826        walker.caller_regs.clear();
1827        let (init, additional) = build_cfi_rules(".cfa: 8 .ra: 12 x11: 16 x12: x11 .cfa +", &[]);
1828        walk_with_stack_cfi(&init, &additional, &mut walker).unwrap();
1829
1830        assert_eq!(walker.caller_regs.len(), 4);
1831        assert_eq!(walker.caller_regs["cfa"], 8);
1832        assert_eq!(walker.caller_regs["ra"], 12);
1833        assert_eq!(walker.caller_regs["x11"], 16);
1834        assert_eq!(walker.caller_regs["x12"], 1608);
1835    }
1836}