Assembly language in zxdb

Disassembly

The disassemble command (di for short) disassembles from the current location. If available, the instructions and call destinations are annotated with source line information:

```none {:.devsite-disable-click-to-copy} [zxdb] di miscsvc.cc:118 ▶ 0x20bc1c7aa60a mov dword ptr [rbx + 0x10c], eax miscsvc.cc:122 0x20bc1c7aa610 movabs rax, -0x5555555555555556 0x20bc1c7aa61a mov qword ptr [rbx + 0xe8], rax 0x20bc1c7aa621 mov qword ptr [rbx + 0xe8], 0x0 0x20bc1c7aa62c mov rdi, qword ptr [rbx + 0xb0] 0x20bc1c7aa633 mov rax, qword ptr [rbx + 0xe8] 0x20bc1c7aa63a mov qword ptr [rbx + 0x20], rax 0x20bc1c7aa63e call 0x20d ➔ std::__2::size<>()

  1. The `di` command can also accept an address or symbol as a parameter. If given a function name,
  2. it disassembles the entire function:
  3. ```none {:.devsite-disable-click-to-copy}
  4. [zxdb] di main
  5. miscsvc.cc:88
  6. 0x20bc1c7aa000 push rbp
  7. 0x20bc1c7aa001 mov rbp, rsp
  8. 0x20bc1c7aa004 push rbx
  9. 0x20bc1c7aa005 and rsp, -0x20
  10. 0x20bc1c7aa009 sub rsp, 0x140
  11. 0x20bc1c7aa010 mov rbx, rsp
  12. 0x20bc1c7aa013 mov rax, qword ptr fs:[0x10]
  13. ...

Switches

The disassemble command accepts these switches:

  • --num=<lines> or -n <lines>: The number of lines or instructions to emit. Defaults to the instructions in the given function (if the location is a function name), or 16 otherwise.

  • --raw or -r: Output raw bytes in addition to the decoded instructions.

Stepping in machine instructions

Machine instructions can be stepped using the following Zxdb commands:

  • nexti / ni: Step to the next instruction, stepping over function calls.

  • stepi / si: Step the next instruction, following function calls.

For example:

```none {:.devsite-disable-click-to-copy} [zxdb] ni 🛑 main(int, const char**) • main.cc:102 main.cc:99 ▶ 0x23f711346233 mov edx, 0x20 0x23f711346238 call 0x35a3a3 ➔ __asan_memcpy 0x23f71134623d mov rdi, qword ptr [rbx + 0x258] 0x23f711346244 call 0x1677 ➔ $anon::DecodeCommandLine

[zxdb] ni 🛑 main(int, const char**) • main.cc:102 main.cc:99 ▶ 0x23f711346238 call 0x35a3a3 ➔ __asan_memcpy 0x23f71134623d mov rdi, qword ptr [rbx + 0x258] 0x23f711346244 call 0x1677 ➔ $anon::DecodeCommandLine 0x23f711346249 mov rdi, qword ptr [rbx + 0x260]

  1. Zxdb maintains information about whether the last command was an assembly command or a source-code
  2. and shows that information on stepping or breakpoint hits. To switch to assembly-language mode,
  3. type `disassemble`, and to switch back to source-code mode, type `list`.
  4. ## Registers
  5. The `regs` command shows the most common CPU registers.
  6. ```none {:.devsite-disable-click-to-copy}
  7. [zxdb] regs
  8. General Purpose Registers
  9. rax 0xfffffffffffffffa = -6
  10. rbx 0x50b7085b
  11. rcx 0x0 = 0
  12. rdx 0x2023de8c87a0
  13. rsi 0x7fffffffffffffff
  14. rdi 0x50b7085b
  15. rbp 0x224bb1e0b950
  16. rsp 0x224bb1e0b928
  17. ...

There are other categories and options for CPU registers that can be shown by switches to the regs command:

  • --all or -a: Enable all register categories (does not imply -e).

  • --float or -f: Prints the dedicated floating-point registers. In most cases you should use --vector instead because all 64-bit ARM code and most x64 code uses vector registers for floating point.

  • --vector or -v: Prints the vector registers. See below for more details.

  • --debug or -d: Prints the debug registers.

  • --extended or -e: Enables more verbose flag decoding. This enables more information that is not normally useful for everyday debugging. This includes information such as the system level flags within the rflags register for x64.

Registers in expressions

Registers can be used in expressions like variables. The canonical name of a register is $reg(register name).

```none {:.devsite-disable-click-to-copy} [zxdb] print $reg(x3) 79

  1. In addition, the raw register name can be used if there is no variable with the same name:
  2. ```none {:.devsite-disable-click-to-copy}
  3. [zxdb] print x3
  4. 79

Registers can be assigned using the normal expression evaluation syntax:

```none {:.devsite-disable-click-to-copy} [zxdb] print x3 = 0 0

  1. ### Vector registers
  2. The `regs --vector` command displays vector registers in a table according to the current
  3. `vector-format` setting. Use `get vector-format` to see the current value and documentation, and
  4. `set vector-format <new-value>` to set a new vector format. Possible values are:
  5. * `i8` (signed) or `u8` (unsigned): Array of 8-bit integers.
  6. * `i16` (signed) or `u16` (unsigned): Array of 16-bit integers.
  7. * `i32` (signed) or `u32` (unsigned): Array of 32-bit integers.
  8. * `i64` (signed) or `u64` (unsigned): Array of 64-bit integers.
  9. * `i128` (signed) or `u128` (unsigned): Array of 128-bit integers.
  10. * `float`: Array of single-precision floating point.
  11. * `double`: Array of double-precision floating point. This is the default.
  12. ```none {:.devsite-disable-click-to-copy}
  13. [zxdb] set vector-format double
  14. [zxdb] regs -v
  15. Vector Registers
  16. mxcsr 0x1fa0 = 8096
  17. Name [3] [2] [1] [0]
  18. ymm0 0 0 0 0
  19. ymm1 0 0 0 3.14159
  20. ymm2 0 0 0 0
  21. ymm3 0 0 0 0
  22. ...

Vector registers can also be used like arrays in expressions. The vector-format setting controls how each register is converted into an array value. For example, to show the low 32 bits interpreted as a floating-point value of the x86 vector register ymm1:

```none {:.devsite-disable-click-to-copy} [zxdb] set vector-format float

[zxdb] print ymm1[0] 3.14159 ```

When converting to an array, the low bits are assigned to be index 0, increasing from there. Note that the vector register table in regs are displayed with the low values on the right side.