What does gdb 'x' command do?

Learn what does gdb 'x' command do? with practical examples, diagrams, and best practices. Covers c, assembly, gdb development techniques with visual explanations.

Demystifying GDB's 'x' Command: Examining Memory in C and Assembly

Hero image for What does gdb 'x' command do?

Explore the powerful 'x' command in GDB for inspecting memory contents, understanding data types, and debugging C and assembly code effectively.

When debugging C or assembly code with GDB (GNU Debugger), understanding the state of memory is crucial. The x command, short for 'examine', is one of GDB's most versatile tools for peeking directly into memory. It allows you to view memory contents at a specified address, interpret them as various data types, and control the format and quantity of data displayed. This article will guide you through the syntax and common uses of the x command, helping you master memory inspection during your debugging sessions.

Understanding the 'x' Command Syntax

The basic syntax of the x command is x/nfu addr, where each component plays a vital role in how GDB interprets and displays the memory. Let's break down each part:

flowchart TD
    A[x command] --> B{Count 'n'}
    B --> C{Format 'f'}
    C --> D{Size 'u'}
    D --> E[Address 'addr']

    subgraph n [Count]
        n1["Number of units to display (default: 1)"]
    end

    subgraph f [Format]
        f1["x: hex, d: decimal, u: unsigned decimal, o: octal, t: binary, a: address, c: char, s: string, i: instruction"]
    end

    subgraph u [Unit Size]
        u1["b: byte, h: halfword (2 bytes), w: word (4 bytes), g: giant (8 bytes)"]
    end

    subgraph addr [Address]
        addr1["Memory address (variable name, register, expression)"]
    end

Breakdown of the GDB 'x' command syntax components.

  • n (Count): This is an optional decimal integer specifying how many units of memory to display. If omitted, GDB defaults to 1.
  • f (Format): This single-letter code determines how GDB should interpret and print each unit of memory. Common formats include:
    • x: hexadecimal (default)
    • d: signed decimal
    • u: unsigned decimal
    • o: octal
    • t: binary
    • a: address
    • c: character
    • s: null-terminated string
    • i: machine instructions
  • u (Unit Size): This single-letter code specifies the size of each unit of memory to be examined. Common sizes are:
    • b: byte (1 byte)
    • h: halfword (2 bytes)
    • w: word (4 bytes, default on most 32-bit systems)
    • g: giant (8 bytes, default on most 64-bit systems)
  • addr (Address): This is the memory address you want to examine. It can be a variable name, a register (e.g., $rsp), a pointer, or any expression that evaluates to a memory address.

Practical Examples in C

Let's look at some common scenarios for using x when debugging C code.

#include <stdio.h>
#include <stdlib.h>

int global_var = 0xDEADBEEF;

int main() {
    int local_var = 12345;
    char *str = "Hello GDB!";
    int *dynamic_arr = (int *)malloc(3 * sizeof(int));

    if (dynamic_arr == NULL) {
        return 1;
    }

    dynamic_arr[0] = 100;
    dynamic_arr[1] = 200;
    dynamic_arr[2] = 300;

    printf("Global: %x\n", global_var);
    printf("Local: %d\n", local_var);
    printf("String: %s\n", str);
    printf("Dynamic[0]: %d\n", dynamic_arr[0]);

    free(dynamic_arr);
    return 0;
}

A simple C program to demonstrate GDB's 'x' command.

# Compile with debug info
gcc -g -o example example.c

# Start GDB
gdb ./example

# Set a breakpoint at main and run
(gdb) b main
(gdb) r

# Examine global_var as a hexadecimal word
(gdb) x/wx &global_var
0x601040 <global_var>:\t0xdeadbeef

# Examine local_var as a signed decimal word
(gdb) x/dw &local_var
0x7fffffffdecc: 12345

# Examine the string 'str' (null-terminated string)
(gdb) x/s str
0x400714: "Hello GDB!"

# Examine the first 3 words of dynamic_arr as decimal integers
(gdb) x/3dw dynamic_arr
0x602010: 100\t200\t300

# Examine memory at a specific address (e.g., 0x400714) as characters
(gdb) x/10cb 0x400714
0x400714: 72 'H'\t101 'e'\t108 'l'\t108 'l'\t111 'o'\t32 ' '\t71 'G'\t68 'D'\t66 'B'\t33 '!'

# Examine the address of local_var
(gdb) x/a &local_var
0x7fffffffdecc: 0x7fffffffdecc

GDB commands and output for examining memory in the C example.

Inspecting Assembly Code and Registers

The x command is equally powerful for low-level debugging, especially when working with assembly code or understanding how your C code translates to machine instructions.

# Continue from the previous GDB session

# Disassemble the main function to find its start address
(gdb) disas main
Dump of assembler code for function main:
   0x00000000004005d6 <+0>: push   %rbp
   0x00000000004005d7 <+1>: mov    %rsp,%rbp
   ...

# Examine 10 instructions starting from main
(gdb) x/10i main
   0x4005d6 <main>: push   %rbp
   0x4005d7 <main+1>: mov    %rsp,%rbp
   0x4005da <main+4>: sub    $0x30,%rsp
   0x4005de <main+8>: mov    $0x3039,%eax
   0x4005e3 <main+13>: mov    %eax,-0x4(%rbp)
   0x4005e6 <main+16>: movabs $0x400714,%rax
   0x4005f0 <main+26>: mov    %rax,-0x10(%rbp)
   0x4005f4 <main+30>: mov    $0x18,%edi
   0x4005f9 <main+35>: callq  0x4004a0 <malloc@plt>
   0x4005fe <main+40>: mov    %rax,-0x18(%rbp)

# Examine the value of the stack pointer register ($rsp) as a hexadecimal address
(gdb) x/a $rsp
0x7fffffffdeb0: 0x7fffffffdeb0

# Examine the memory pointed to by $rsp as 4 hexadecimal words
(gdb) x/4wx $rsp
0x7fffffffdeb0: 0x00000000\t0x00000000\t0x00000000\t0x00000000

Using 'x' to inspect assembly instructions and register contents.

Common Pitfalls and Tips

While powerful, the x command can sometimes be confusing if you're not careful with its parameters.

Here are some tips for effective use:

  • Default Behavior: If you omit n, f, or u, GDB uses the last specified values for those parameters. This can be convenient but also lead to unexpected output if you forget the previous settings.
  • Address Expressions: GDB's address expressions are very flexible. You can use arithmetic (e.g., &my_array + 4), dereference pointers (e.g., *my_ptr), or combine them with register values (e.g., $rsp + 8).
  • Repetition: Simply pressing Enter after an x command will repeat the last x command, advancing the memory address by the total size of the previously displayed units. This is excellent for scanning through large memory regions.
  • Type Casting: For complex C data structures, it's often easier to print the variable directly using print (struct my_struct)my_var rather than trying to reconstruct it with x. However, x is invaluable for examining raw bytes or when the type information is corrupted or unavailable.