| src | ||
| tests | ||
| vcpkg@3b9d086009 | ||
| .gitignore | ||
| .gitmodules | ||
| CMakeLists.txt | ||
| CMakePresets.json | ||
| README.md | ||
| test_access.c | ||
| test_with_args.c | ||
| vcpkg.json | ||
gwatch - Global Variable Watcher
A tool to monitor reads and writes to global integer variables in Linux binaries using ptrace and hardware watchpoints.
Features
- Monitors specific global integer variables in a running program
- Attempts to set up hardware watchpoints via ptrace to detect variable access
- Supports PIE (Position Independent Executable) binaries
- Passes command-line arguments to the target program
- Uses DWARF debug information to locate variables
Building
cmake -B build
cmake --build build
Usage
gwatch --var <symbol> --exec <path> [-- arg1 arg2 ... argN]
Arguments:
--var <symbol>: Name of the global variable to watch (required)--exec <path>: Path to the executable to monitor (required)-- arg1 arg2 ...: Optional arguments to pass to the target program
Note: The target binary must be compiled with debug symbols (-g flag).
Examples:
# Watch global_counter in test_access
./build/gwatch --var global_counter --exec ./test_access
# Watch with program arguments
./build/gwatch --var global_counter --exec ./test_with_args -- hello world 123
Output Format
When watchpoints trigger, gwatch outputs access events in the following format:
For writes:
<symbol>\twrite\t<old_value>-><new_value>
For reads:
<symbol>\tread\t<value>
Example output:
global_counter read 0
global_counter write 0->42
global_counter read 42
global_counter write 42->52
Implementation Details
Variable Detection
- Uses libdwarf to parse DWARF debug information
- Identifies global integer variables (int, long, short, char, unsigned variants)
- Handles const/volatile type qualifiers
- Retrieves variable address and size from debug info
Hardware Watchpoints
The tool uses x86-64 hardware debug registers (DR0-DR7) via ptrace to set watchpoints:
- DR0: Stores the watched address
- DR7: Configures watchpoint type (read/write) and size
- DR6: Status register checked on SIGTRAP to detect watchpoint hits
PIE Binary Support
For Position Independent Executables, the tool:
- Reads
/proc/[pid]/mapsto find the actual load address - Adds the load address to the DWARF-provided offset
- Sets the watchpoint at the runtime address
Read vs Write Detection
When a watchpoint triggers:
- The current value is read from the traced process memory using
ptrace(PTRACE_PEEKDATA) - If the value has changed since the last access, it's reported as a write
- If the value is unchanged, it's reported as a read
- Values are formatted according to the variable size (1, 2, 4, or 8 bytes)
Current Limitations
Hardware watchpoints are not currently triggering on the test system. The implementation follows standard Linux ptrace documentation for setting x86 hardware breakpoints, but watchpoint events are not being generated. This may be due to:
- Kernel configuration (hardware breakpoints disabled or restricted)
- Security settings (e.g.,
/proc/sys/kernel/yama/ptrace_scope) - System-specific ptrace behavior variations
- Potential need for additional ptrace options or setup steps
The code correctly:
- Sets DR0 to the target address
- Configures DR7 with proper enable bits, condition codes, and length fields
- Monitors for SIGTRAP signals
- Checks DR6 for watchpoint hits
But SIGTRAP signals are never generated when the watched variable is accessed.
Testing
Test programs are provided:
test_access.c: Program that reads and writes toglobal_countertest_with_args.c: Program that accepts command-line arguments and modifiesglobal_counter
Compile test programs with:
gcc -g -O0 -o test_access test_access.c
gcc -g -O0 -o test_with_args test_with_args.c
Run tests:
# Basic test
./build/gwatch --var global_counter --exec ./test_access
# Test with arguments
./build/gwatch --var global_counter --exec ./test_with_args -- hello world 123
Future Work
- Investigate alternative watchpoint implementations
- Add support for watching non-integer types
- Support multiple simultaneous watchpoints (using DR1-DR3)
- Add filtering options (read-only vs write-only vs read/write)
- Better error reporting and diagnostics