rnd's website logotrying to do assembly-level debugging in gdb

GDB seems to have been clearly made with source-level debugging in mind, the kind where the source code of the program is available. Sometimes this is not the case, or you just don't want to bother looking for the source code. Here I'll put a bunch of useful commands.

  • layout asm: enable TUI mode and show assembly instructions

  • layout regs: enable TUI mode and show CPU registers

  • set disassembly-flavor intel: use Intel-like disassembly, which is more familiar to those writing or debugging assembly code on Windows

  • Ctrl+P, Ctrl+N: serve as alternatives to "up" and "down" when the active TUI window is not the command window

  • Ctrl+X, O: switch the active TUI window

  • Ctrl+X, (A or Shift+A or Ctrl+A): toggle TUI mode

  • x/5i $pc: check out 5 instructions, starting at the program counter

  • ni (nexti) and si (stepi): next/step, but on an instruction level

  • b *0x<addr>: breakpoint on a specific address

  • info registers: look at CPU registers

  • info frame: look at current stack frame, potentially useful for finding function arguments and local vars

  • x/8g $sp: look at (64-bit words) in the stack

x64 Linux C/C++ conventions

This is a set of additional notes written during the process of disassembling a library.

For function calls, the arguments are stored in the following order:

  • RDI register
  • RSI register
  • RDX register
  • RCX register
  • R8 register
  • R9 register
  • rest are pushed onto the stack in reverse order

When a function is called, it will typically perform the following operations:

push rbp
mov  rbp, rsp
sub  rsp, (amount of space used for local variables)
...
add  rsp, (same amount)
leave ; or mov rsp,rbp and pop rbp
ret

This means that any reference to memory at [rbp-0x...] is using the function's local variables.

If a function returns a value, it will be stored in the EAX register.

If it is a C++ function, its name will be "mangled": its argument types will be included in the name. The utility c++filt can convert these mangled names to their standard forms. If the function is part of an object, it will require the first parameter to be the object's address.

Seems like the same is true for constructors and destructors: their first parameter is an address that will point to the object in question.

Follow me on Mastodon This page made with Vim Best viewed in Firefox