NationalSecurityAgency/ghidra · error · IllegalArgumentException

Cannot work with sub-byte registers. Consider a parent inste

Error message

Cannot work with sub-byte registers. Consider a parent instead.

What it means

TraceRegisterUtils.requireByteBound rejects registers whose bit boundaries do not align to byte boundaries. A register is byte-bound only if both its least-significant-bit position and its total bit length are multiples of 8. Sub-byte registers (e.g. flag bits, nibbles) cannot be handled by byte-oriented memory operations and require working through their parent register instead.

Source

Thrown at Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/TraceRegisterUtils.java:297

		buf.limit(buf.position() + byteLength);
		return buf;
	}

	public static RegisterValue finishBuffer(ByteBuffer buf, Register register) {
		byte[] arr = buf.array();
		if (!register.isBigEndian() && !register.isProcessorContext()) {
			ArrayUtils.reverse(arr, register.getBaseMask().length, buf.capacity());
		}
		return new RegisterValue(register, arr);
	}

	public static boolean isByteBound(Register register) {
		return register.getLeastSignificantBit() % 8 == 0 && register.getBitLength() % 8 == 0;
	}

	public static void requireByteBound(Register register) {
		if (!isByteBound(register)) {
			throw new IllegalArgumentException(
				"Cannot work with sub-byte registers. Consider a parent instead.");
		}
	}

	public static TraceRegisterContainer getRegisterContainer(TraceThread thread, int frameLevel) {
		return getRegisterContainer(thread.getObject(), frameLevel);
	}

	public static TraceRegisterContainer getRegisterContainer(TraceStackFrame frame) {
		// Use frameLevel = 0, because we're already in the frame
		// so, no wild cards between here and registers
		return getRegisterContainer(frame.getObject(), 0);
	}

	public static TraceRegisterContainer getRegisterContainer(TraceObject object, int frameLevel) {
		if (object.getSchema()
				.getInterfaces()
				.contains(TraceRegisterContainer.class)) {

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Use the parent register instead: register.getBaseRegister() or register.getParentRegister() to get a byte-aligned container
  2. Use isByteBound() to check before calling requireByteBound()
  3. For flag-bit access, read the full status register and mask/shift
  4. If you need sub-byte access, use RegisterValue APIs that operate on bit masks rather than byte arrays

Example fix

// before
TraceRegisterUtils.requireByteBound(x86_CF); // carry flag is 1 bit
// after
Register parent = x86_CF.getParentRegister(); // e.g. full flags register
TraceRegisterUtils.requireByteBound(parent);
Defensive patterns

Strategy: validation

Validate before calling

boolean isSafeForByteOps(Register register) {
    return TraceRegisterUtils.isByteBound(register);
}

// or inline:
boolean byteBound = register.getLeastSignificantBit() % 8 == 0
    && register.getBitLength() % 8 == 0;

Type guard

// N/A — Register is a single type; use runtime bit-length check

Try / catch

try {
    TraceRegisterUtils.requireByteBound(register);
} catch (IllegalArgumentException e) {
    register = register.getParentRegister();
    TraceRegisterUtils.requireByteBound(register);
}

Prevention

When it happens

Trigger: Calling requireByteBound(register) where register.getLeastSignificantBit() % 8 != 0 (e.g. bit 3 of a byte) or register.getBitLength() % 8 != 0 (e.g. a 4-bit or 12-bit register). Common with x86 flag registers, ARM condition flags, or any architecture with sub-byte fields.

Common situations: Attempting to read/write a status flag register (like x86 CF, ZF) directly. Working with processor context fields that are smaller than a byte. Using a register obtained from a bit-field definition rather than a byte-aligned one.

Related errors


AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14). Data as JSON: /api/errors/ace02db4af1b513d. Report an issue: GitHub.