Switch to lokkju/dosbox-x-remotedebug fork for working GDB server

- Use remotedebug branch which adds GDB/QMP server support
- Build with --enable-remotedebug configure flag
- Add proper GDB handshake (qSupported) to gdb_client.py
- Support no-ack mode for faster communication
- Expose QMP port 4444 in addition to GDB port 1234

The GDB server is configured via [dosbox] section:
  gdbserver = true
  gdbserver port = 1234
  qmpserver = true
  qmpserver port = 4444

Tested features: register read, memory read, breakpoints
This commit is contained in:
Ryan Malloy 2026-01-27 14:55:53 -07:00
parent 6b5057c17a
commit b022f6fb9e
3 changed files with 59 additions and 17 deletions

View File

@ -29,18 +29,21 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \ ca-certificates \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Clone DOSBox-X (main repo - check for GDB support) # Clone DOSBox-X with remote debugging support (GDB server + QMP)
# Note: If hezi/dosbox-x-gdb is stale, main DOSBox-X may have debugger support # This fork adds --enable-remotedebug which compiles in gdbserver.cpp and qmp.cpp
# See: https://github.com/joncampbell123/dosbox-x/issues/752
WORKDIR /build WORKDIR /build
RUN git clone --depth 1 https://github.com/joncampbell123/dosbox-x.git RUN git clone --branch remotedebug --depth 1 https://github.com/lokkju/dosbox-x-remotedebug.git dosbox-x
WORKDIR /build/dosbox-x WORKDIR /build/dosbox-x
# Configure and build # Configure and build with GDB server support
# DOSBox-X has built-in debugger that can be enabled # --enable-remotedebug: Enables C_REMOTEDEBUG flag for GDB/QMP servers
# --enable-debug: Enables internal debugger (Alt+Pause)
RUN ./autogen.sh && \ RUN ./autogen.sh && \
./configure \ ./configure \
--prefix=/opt/dosbox-x \ --prefix=/opt/dosbox-x \
--enable-remotedebug \
--enable-debug \ --enable-debug \
--enable-sdl2 \ --enable-sdl2 \
--disable-printer \ --disable-printer \
@ -65,6 +68,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libavformat59 \ libavformat59 \
libavutil57 \ libavutil57 \
libswscale6 \ libswscale6 \
libgl1 \
libx11-6 \
libncurses6 \
libxkbcommon0 \
libxrandr2 \
libxi6 \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Copy DOSBox-X from builder # Copy DOSBox-X from builder
@ -76,7 +85,8 @@ RUN ln -s /opt/dosbox-x/bin/dosbox-x /usr/local/bin/dosbox-x
# Create directories for config and DOS files # Create directories for config and DOS files
RUN mkdir -p /config /dos RUN mkdir -p /config /dos
# Default configuration with GDB stub enabled # Default configuration for DOSBox-X
# GDB server is enabled via --enable-remotedebug at compile time
RUN cat > /config/dosbox.conf << 'EOF' RUN cat > /config/dosbox.conf << 'EOF'
[sdl] [sdl]
fullscreen=false fullscreen=false
@ -91,11 +101,6 @@ cycles=auto
[dosbox] [dosbox]
memsize=16 memsize=16
[debugger]
# Enable GDB server stub
gdbserver=true
gdbport=1234
[serial] [serial]
serial1=disabled serial1=disabled
serial2=disabled serial2=disabled
@ -106,8 +111,10 @@ MOUNT C /dos
C: C:
EOF EOF
# Expose GDB port # Expose ports:
EXPOSE 1234 # - 1234: GDB server (standard GDB remote protocol)
# - 4444: QMP server (QEMU Machine Protocol for control)
EXPOSE 1234 4444
# Set working directory # Set working directory
WORKDIR /dos WORKDIR /dos
@ -116,5 +123,6 @@ WORKDIR /dos
ENV DISPLAY=:0 ENV DISPLAY=:0
# Entry point # Entry point
# Note: GDB/QMP servers are enabled via config file [dosbox] section
ENTRYPOINT ["dosbox-x", "-conf", "/config/dosbox.conf"] ENTRYPOINT ["dosbox-x", "-conf", "/config/dosbox.conf"]
CMD [] CMD []

View File

@ -16,10 +16,10 @@ services:
dockerfile: Dockerfile dockerfile: Dockerfile
container_name: dosbox-mcp container_name: dosbox-mcp
# Ports # Ports (remotedebug fork exposes these when built with --enable-remotedebug)
ports: ports:
- "${GDB_PORT:-1234}:1234" # GDB stub - "${GDB_PORT:-1234}:1234" # GDB server (standard GDB remote protocol)
- "${SERIAL_PORT:-5555}:5555" # Serial (optional) - "${QMP_PORT:-4444}:4444" # QMP server (QEMU Machine Protocol)
# X11 forwarding for display # X11 forwarding for display
environment: environment:
@ -74,7 +74,8 @@ services:
- headless - headless
ports: ports:
- "${GDB_PORT_HEADLESS:-1235}:1234" - "${GDB_PORT_HEADLESS:-1235}:1234" # GDB server
- "${QMP_PORT_HEADLESS:-4445}:4444" # QMP server
environment: environment:
# Use virtual framebuffer for headless # Use virtual framebuffer for headless

View File

@ -120,10 +120,43 @@ class GDBClient:
finally: finally:
self._socket.settimeout(self.timeout) self._socket.settimeout(self.timeout)
# Perform GDB protocol handshake (required by some stubs)
self._handshake()
except OSError as e: except OSError as e:
self._connected = False self._connected = False
raise GDBError(f"Failed to connect to {host}:{port}: {e}") from e raise GDBError(f"Failed to connect to {host}:{port}: {e}") from e
def _handshake(self) -> None:
"""Perform GDB protocol handshake.
Some GDB stubs (like lokkju/dosbox-x-remotedebug) require qSupported
as the first packet to establish the session.
"""
try:
# Send qSupported to establish capabilities
features = "qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+"
response = self._command(features)
logger.debug(f"qSupported response: {response}")
# Parse supported features
if response:
supported = response.split(';')
logger.info(f"GDB stub supports: {supported[:5]}...") # First 5
# Enable no-ack mode if supported (faster communication)
if "QStartNoAckMode+" in supported:
try:
ack_response = self._command("QStartNoAckMode")
if ack_response == "OK":
logger.info("No-ack mode enabled")
except GDBError:
pass # Not critical
except GDBError as e:
# Handshake failure - stub may not support qSupported
logger.warning(f"Handshake failed (may be normal): {e}")
def disconnect(self) -> None: def disconnect(self) -> None:
"""Disconnect from GDB stub.""" """Disconnect from GDB stub."""
if self._socket: if self._socket: