Work with assembly language

disassemble

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

For example:

disassemble
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<>()

Functions

If you specify an address or symbol, the disassemble command disassembles on the respective address or symbol. If you provide a function name, it disassembles the entire function:

disassemble main
miscsvc.cc:88
   0x20bc1c7aa000  push    rbp
   0x20bc1c7aa001  mov     rbp, rsp
   0x20bc1c7aa004  push    rbx
   0x20bc1c7aa005  and     rsp, -0x20
   0x20bc1c7aa009  sub     rsp, 0x140
   0x20bc1c7aa010  mov     rbx, rsp
   0x20bc1c7aa013  mov     rax, qword ptr fs:[0x10]
   ...

PC relative offsets

In some cases, you may want to disassemble based on the PC (program counter) relative offset.

For example, to disassemble at the address $rip - 0x7:

[zxdb] di -- -0x7 # Disassemble at the address $rip - 0x7
     350     FX_LOGS(FATAL) << "Failed to construct the cobalt app: " << app.status();
     351   }
     352   inspector.Health().Ok();
     353   loop.Run();
   0x591e76352b  xor   edx, edx
   0x591e76352d  call  0x260fae    async::Loop::Run(async::Loop*, zx::time, bool)
     354   FX_LOGS(INFO) << "Cobalt will now shut down.";
  0x591e763532  mov   edi, 0x30
   0x591e763537  call  0x81ab4     fuchsia_logging::ShouldCreateLogMessage(fuchsia_logging::LogSeverity)
   0x591e76353c  mov   byte ptr [rbp - 0x1b1], 0x0
   0x591e763543  test  al, 0x1
   0x591e763545  jne   0x2
   0x591e763547  jmp   0x74

Arguments

The disassemble command accepts the following arguments:

  • --num=<lines> or -n <lines>: The number of lines or instructions to emit. If the location is a function name, it defaults to the instructions in the given function. If the location is an address or symbol, it defaults to 16.

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

Stepping in machine instructions

To step through machine instructions you can use following zxdb commands:

  • nexti / ni: Advances to the next instruction, but steps over call instructions for the target architecture..

  • stepi / si: Advances exactly one machine instruction..

For example:

nexti
🛑 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] nexti
🛑 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]

Zxdb maintains information about whether the last command was an assembly command or source-code and shows that information on stepping or breakpoint hits.

To switch to assembly-language mode, use disassemble.

To switch to source-code mode, use list.

regs

The regs command shows the most common CPU registers.

For example:

regs
General Purpose Registers
      rax  0xfffffffffffffffa = -6
      rbx          0x50b7085b
      rcx                 0x0 = 0
      rdx      0x2023de8c87a0
      rsi  0x7fffffffffffffff
      rdi          0x50b7085b
      rbp      0x224bb1e0b950
      rsp      0x224bb1e0b928
      ...

The regs command accepts the following arguments:

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

  • --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.

  • --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.

Registers in expressions

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

For more information and examples, see CPU registers.

Vector registers

The regs --vector command displays vector registers in a table according to the current vector-format setting.

Use get vector-format to see the current values.

Use set vector-format <new-value> to set a new vector format.

The following values are supports:

  • i8 (signed) or u8 (unsigned): Array of 8-bit integers.
  • i16 (signed) or u16 (unsigned): Array of 16-bit integers.
  • i32 (signed) or u32 (unsigned): Array of 32-bit integers.
  • i64 (signed) or u64 (unsigned): Array of 64-bit integers.
  • i128 (signed) or u128 (unsigned): Array of 128-bit integers.
  • float: Array of single-precision floating point.
  • double: Array of double-precision floating point. This is the default.

This example sets the vector-format to double:

set vector-format double

This example returns the vector registers:

[zxdb] regs -v
Vector Registers
  mxcsr 0x1fa0 = 8096

   Name [3] [2] [1]       [0]
   ymm0   0   0   0         0
   ymm1   0   0   0   3.14159
   ymm2   0   0   0         0
   ymm3   0   0   0         0
   ...

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:

  1. Set the vector-format to float:

    set vector-format float
    
  2. Print the vector register ymm1:

    [zxdb] print ymm1[0]
    3.14159
    

When converting to an array, the low bits are assigned to be index 0, increasing from there.