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(®[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 == 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}