| # Work with assembly language |
| |
| ## `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: |
| |
| ```none {: .devsite-terminal data-terminal-prefix="[zxdb]" } |
| 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: |
| |
| ```none {: .devsite-terminal data-terminal-prefix="[zxdb]" } |
| 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`: |
| |
| 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 |
| ``` |
| |
| ### 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: |
| |
| ```none {: .devsite-terminal data-terminal-prefix="[zxdb]" } |
| 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: |
| |
| ```none {: .devsite-terminal data-terminal-prefix="[zxdb]" } |
| 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](expressions.md) like variables. The |
| canonical name of a register is `$reg(register name)`. |
| |
| For more information and examples, see [CPU registers][cpu-registers-doc]. |
| |
| ### 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`: |
| |
| ```none {: .devsite-terminal data-terminal-prefix="[zxdb]" } |
| 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`: |
| |
| |
| 1. Set the `vector-format` to float: |
| |
| ```none {: .devsite-terminal data-terminal-prefix="[zxdb]" } |
| set vector-format float |
| ``` |
| |
| 1. 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. |
| |
| [cpu-registers-doc]: /docs/development/debugger/expressions.md#cpu-registers |