disassemble
Note: This is the disassemble
command. You can also use di
to express 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<>()
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] ...
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
:
Note: $rip
is a register that stores a number (an address) of the next instruction to execute.
[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
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.
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 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.
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:
Note: The vector register table in regs
are displayed with the low values on the right side.
[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
:
vector-format
to float:set vector-format float
ymm1
:[zxdb] print ymm1[0] 3.14159
When converting to an array, the low bits are assigned to be index 0, increasing from there.