# How to Configure the Kernel GDB Debug Stub in vphone‑cli

> Configure the kernel GDB debug stub in vphone-cli using the VZGDBDebugStubConfiguration API. Set an optional TCP port or use automatic allocation for seamless debugging.

- Repository: [Lakr/vphone-cli](https://github.com/Lakr233/vphone-cli)
- Tags: how-to-guide
- Published: 2026-09-06

---

**The kernel GDB debug stub in vphone‑cli is configured via the private `VZGDBDebugStubConfiguration` API, with an optional explicit TCP port (6000–65535) or system‑assigned automatic port allocation.**

vphone‑cli provides a GDB-compatible debug stub for low‑level kernel debugging of virtual iPhone instances. The stub leverages Apple's private `VZGDBDebugStubConfiguration` class within Virtualization.framework, exposed through dynamic runtime calls. This article breaks down exactly how the stub is configured, validated, and attached to the VM.

## Kernel GDB Debug Stub Configuration Architecture

The debug stub configuration follows a two‑tier design: **explicit port mode** and **system‑assigned port mode**. Both paths are implemented in [`VPhoneVirtualMachine.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneVirtualMachine.swift), while user-facing CLI options reside in [`VPhoneCLI.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneCLI.swift) and [`VPhoneVMLaunchCLI.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneVMLaunchCLI.swift).

### Explicit Port Configuration

When the `--kernel-debug-port` option is provided, vphone‑cli creates the stub with a user‑specified TCP port. The implementation validates the port range before instantiation.

```swift
// VPhoneVirtualMachine.swift – explicit port validation and stub creation
if let kernelDebugPort = options.kernelDebugPort {
    guard (6000...65535).contains(kernelDebugPort) else {
        throw VPhoneError.invalidKernelDebugPort(kernelDebugPort)
    }
    if let kernelDebugStub = Dynamic._VZGDBDebugStubConfiguration(port: kernelDebugPort).asObject {
        Dynamic(config)._setDebugStub(kernelDebugStub)
        print("[vphone] Kernel GDB debug stub: tcp://127.0.0.1:\(kernelDebugPort)")
    } else {
        // Fallback to system-assigned if private API fails
        Dynamic(config)._setDebugStub(Dynamic._VZGDBDebugStubConfiguration().asObject)
        print("[vphone] Kernel GDB debug stub enabled (system‑assigned port)")
    }
}

```

**Key validation**: Ports outside 6000–65535 trigger `VPhoneError.invalidKernelDebugPort`, preventing bind failures or privileged port conflicts.

### System‑Assigned Port Configuration

When no port is specified, or when the explicit stub creation fails, vphone‑cli invokes `Dynamic._VZGDBDebugStubConfiguration()` with no arguments. This delegates port selection to macOS, which allocates an available ephemeral port.

```swift
// VPhoneVirtualMachine.swift – system-assigned stub fallback
} else {
    Dynamic(config)._setDebugStub(Dynamic._VZGDBDebugStubConfiguration().asObject)
    print("[vphone] Kernel GDB debug stub enabled (system‑assigned port)")
}

```

## Runtime Port Discovery on macOS 26+

On macOS 26 and later, vphone‑cli can query the actual listening port after VM startup. This resolves the ambiguity of system‑assigned ports.

```swift
// VPhoneVirtualMachine.swift – retrieving auto-assigned port post-launch
if ProcessInfo.processInfo.operatingSystemVersion.majorVersion >= 26,
   let debugStub = Dynamic(vm)._configuration._debugStub.asAnyObject,
   let port = Dynamic(debugStub).port.asInt, port > 0 {
    print("[vphone] Kernel GDB debug stub listening on tcp://127.0.0.1:\(port)")
}

```

The `Dynamic(vm)._configuration._debugStub` property exposes the configured stub object, and `.port.asInt` retrieves the bound TCP port.

## CLI Option Definitions

The `--kernel-debug-port` option is surfaced through two command entry points depending on the operational mode.

### Boot Command ([`VPhoneCLI.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneCLI.swift))

```swift
// VPhoneCLI.swift – boot subcommand option
@Option(help: "Kernel GDB debug stub port on host (omit for system-assigned port; valid: 6000...65535)")
var kernelDebugPort: Int?

```

### Launch Command ([`VPhoneVMLaunchCLI.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneVMLaunchCLI.swift))

```swift
// VPhoneVMLaunchCLI.swift – launch subcommand option
@Option(help: "Kernel GDB debug stub port on host (omit for system-assigned; valid: 6000...65535)")
var kernelDebugPort: Int?

```

Both options forward the value to `VPhoneVMOption` struct, which [`VPhoneVirtualMachine.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneVirtualMachine.swift) consumes during VM construction.

## Practical Usage Examples

### Launch with Explicit GDB Stub Port

Bind the stub to a fixed port for predictable debugger attachment:

```bash
vphone-cli boot --config ./config.plist --kernel-debug-port 6001

```

**Expected output**:

```

[vphone] Kernel GDB debug stub: tcp://127.0.0.1:6001

```

### Launch with System‑Assigned Port

Omit the flag to let macOS allocate the port automatically:

```bash
vphone-cli boot --config ./config.plist

```

**Expected output** (macOS 26+):

```

[vphone] Kernel GDB debug stub enabled (system‑assigned port)
[vphone] Kernel GDB debug stub listening on tcp://127.0.0.1:52713

```

### Connecting GDB to the Stub

Attach GDB to the exposed stub endpoint:

```bash
gdb -ex "target remote 127.0.0.1:52713" \
    -ex "symbol-file /path/to/kernel.symbols" \
    -ex "continue"

```

Replace `52713` with your actual port number.

## Source File Reference

| File | Responsibility |
|------|---------------|
| [`VPhoneVirtualMachine.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneVirtualMachine.swift) | Core stub configuration, validation, and runtime port query |
| [`VPhoneCLI.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneCLI.swift) | `--kernel-debug-port` option for `boot` subcommand |
| [`VPhoneVMLaunchCLI.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneVMLaunchCLI.swift) | `--kernel-debug-port` option for `launch` subcommand |

## Summary

- **Explicit ports** (6000–65535) are validated and passed to `Dynamic._VZGDBDebugStubConfiguration(port:)`
- **System-assigned ports** use the parameterless `Dynamic._VZGDBDebugStubConfiguration()` initializer
- **macOS 26+** allows post-startup port discovery via `Dynamic(vm)._configuration._debugStub.port`
- **CLI exposure** is unified across `boot` and `launch` subcommands in [`VPhoneCLI.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneCLI.swift) and [`VPhoneVMLaunchCLI.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneVMLaunchCLI.swift)
- **Error handling** throws `VPhoneError.invalidKernelDebugPort` for out-of-range values

## Frequently Asked Questions

### What is the valid port range for the kernel GDB debug stub?

The valid range is **6000 to 65535** inclusive. Ports below 6000 are rejected to avoid conflicts with well-known services, and the upper bound matches the maximum TCP port number. Supplying a port outside this range triggers `VPhoneError.invalidKernelDebugPort`.

### How do I find the system-assigned port on older macOS versions?

On macOS versions prior to 26, vphone‑cli cannot query the auto-assigned port at runtime. You must either use an explicit port (`--kernel-debug-port`), or inspect system network state with `lsof -iTCP -sTCP:LISTEN -P | grep vphone` or similar tools to identify the listening port.

### Why does vphone‑cli use private APIs for the debug stub?

Apple does not expose `VZGDBDebugStubConfiguration` in the public Virtualization.framework headers. Vphone‑cli accesses this functionality through **dynamic Objective-C runtime calls** (`Dynamic._VZGDBDebugStubConfiguration`), which enables kernel debugging capabilities otherwise unavailable to sandboxed or standard API usage.

### Can I disable the kernel GDB debug stub entirely?

No—the current implementation in [`VPhoneVirtualMachine.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneVirtualMachine.swift) always attaches a stub. When `--kernel-debug-port` is omitted, a system-assigned stub is configured automatically. There is no explicit "disable" flag in the source code.