{"record":{"id":"ace02db4af1b513d","repo":"NationalSecurityAgency/ghidra","slug":"cannot-work-with-sub-byte-registers-consider-a-pa","errorCode":null,"errorMessage":"Cannot work with sub-byte registers. Consider a parent instead.","messagePattern":"Cannot work with sub-byte registers\\. Consider a parent instead\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/TraceRegisterUtils.java","lineNumber":297,"sourceCode":"\t\tbuf.limit(buf.position() + byteLength);\n\t\treturn buf;\n\t}\n\n\tpublic static RegisterValue finishBuffer(ByteBuffer buf, Register register) {\n\t\tbyte[] arr = buf.array();\n\t\tif (!register.isBigEndian() && !register.isProcessorContext()) {\n\t\t\tArrayUtils.reverse(arr, register.getBaseMask().length, buf.capacity());\n\t\t}\n\t\treturn new RegisterValue(register, arr);\n\t}\n\n\tpublic static boolean isByteBound(Register register) {\n\t\treturn register.getLeastSignificantBit() % 8 == 0 && register.getBitLength() % 8 == 0;\n\t}\n\n\tpublic static void requireByteBound(Register register) {\n\t\tif (!isByteBound(register)) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Cannot work with sub-byte registers. Consider a parent instead.\");\n\t\t}\n\t}\n\n\tpublic static TraceRegisterContainer getRegisterContainer(TraceThread thread, int frameLevel) {\n\t\treturn getRegisterContainer(thread.getObject(), frameLevel);\n\t}\n\n\tpublic static TraceRegisterContainer getRegisterContainer(TraceStackFrame frame) {\n\t\t// Use frameLevel = 0, because we're already in the frame\n\t\t// so, no wild cards between here and registers\n\t\treturn getRegisterContainer(frame.getObject(), 0);\n\t}\n\n\tpublic static TraceRegisterContainer getRegisterContainer(TraceObject object, int frameLevel) {\n\t\tif (object.getSchema()\n\t\t\t\t.getInterfaces()\n\t\t\t\t.contains(TraceRegisterContainer.class)) {","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/TraceRegisterUtils.java#L279-L315","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use the parent register instead: register.getBaseRegister() or register.getParentRegister() to get a byte-aligned container","Use isByteBound() to check before calling requireByteBound()","For flag-bit access, read the full status register and mask/shift","If you need sub-byte access, use RegisterValue APIs that operate on bit masks rather than byte arrays"],"exampleFix":"// before\nTraceRegisterUtils.requireByteBound(x86_CF); // carry flag is 1 bit\n// after\nRegister parent = x86_CF.getParentRegister(); // e.g. full flags register\nTraceRegisterUtils.requireByteBound(parent);","handlingStrategy":"validation","validationCode":"boolean isSafeForByteOps(Register register) {\n    return TraceRegisterUtils.isByteBound(register);\n}\n\n// or inline:\nboolean byteBound = register.getLeastSignificantBit() % 8 == 0\n    && register.getBitLength() % 8 == 0;","typeGuard":"// N/A — Register is a single type; use runtime bit-length check","tryCatchPattern":"try {\n    TraceRegisterUtils.requireByteBound(register);\n} catch (IllegalArgumentException e) {\n    register = register.getParentRegister();\n    TraceRegisterUtils.requireByteBound(register);\n}","preventionTips":["Check isByteBound before byte-oriented register operations","Use parent/base registers for sub-byte fields","Avoid direct byte-level access on flag or context registers"],"tags":["ghidra","trace-utils","register","byte-alignment","precondition"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}