GP-5013 Rework disassembler to address special use cases

This commit is contained in:
ghidra1 2024-10-14 19:04:06 -04:00
parent d97df93cd5
commit d5ae0f96e8

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -680,7 +680,20 @@ public class Disassembler implements DisassemblerConflictHandler {
while ((nextBlock = disassemblerQueue.getNextBlockToBeDisassembled(fallThruAddr, while ((nextBlock = disassemblerQueue.getNextBlockToBeDisassembled(fallThruAddr,
programMemBuffer.getMemory(), monitor)) != null) { programMemBuffer.getMemory(), monitor)) != null) {
Address blockAddr = disassemblerQueue.getDisassemblyAddress(); Address blockAddr = nextBlock.getStartAddress();
Address flowFrom = nextBlock.getFlowFromAddress();
if (flowFrom != null) {
InstructionBlock containingBlock =
instructionSet.getInstructionBlockContaining(blockAddr);
if (containingBlock != null && containingBlock.getInstructionAt(flowFrom) != null) {
// Skip block if start address already handled as a fallthrough.
// NOTE: This behavior is relied upon by customized disassemblers which may
// create a conjoined InstructionBlock for an unconditional branch which
// behaves like a fallthrough flow.
continue;
}
}
if (!disassemblerContext.isFlowActive()) { if (!disassemblerContext.isFlowActive()) {
disassemblerContext.flowStart(blockAddr); disassemblerContext.flowStart(blockAddr);
@ -932,6 +945,7 @@ public class Disassembler implements DisassemblerConflictHandler {
while (!monitor.isCancelled() && addr != null) { while (!monitor.isCancelled() && addr != null) {
if (restrictedAddressSet != null && !restrictedAddressSet.contains(addr)) { if (restrictedAddressSet != null && !restrictedAddressSet.contains(addr)) {
blockTerminated(block);
return; // no fall-through return; // no fall-through
} }
@ -941,16 +955,13 @@ public class Disassembler implements DisassemblerConflictHandler {
new WrappedMemBuffer(blockMemBuffer, DISASSEMBLE_MEMORY_CACHE_SIZE, new WrappedMemBuffer(blockMemBuffer, DISASSEMBLE_MEMORY_CACHE_SIZE,
(int) addr.subtract(blockMemBuffer.getAddress())); (int) addr.subtract(blockMemBuffer.getAddress()));
adjustPreParseContext(instrMemBuffer); InstructionPrototype prototype = parseInstructionPrototype(instrMemBuffer, block);
RegisterValue contextValue = null; RegisterValue contextValue = null;
if (baseContextRegister != null) { if (baseContextRegister != null) {
contextValue = disassemblerContext.getRegisterValue(baseContextRegister); contextValue = disassemblerContext.getRegisterValue(baseContextRegister);
} }
InstructionPrototype prototype =
language.parse(instrMemBuffer, disassemblerContext, false);
// if fall-through already exists in another block - check for conflict // if fall-through already exists in another block - check for conflict
// and terminate terminate block // and terminate terminate block
if (!block.isEmpty() && instructionSet != null && if (!block.isEmpty() && instructionSet != null &&
@ -960,13 +971,14 @@ public class Disassembler implements DisassemblerConflictHandler {
InstructionPrototype existingProto = existingBlockStartInstr.getPrototype(); InstructionPrototype existingProto = existingBlockStartInstr.getPrototype();
if (!existingProto.equals(prototype)) { if (!existingProto.equals(prototype)) {
PseudoInstruction badInst = getPseudoInstruction(addr, prototype, PseudoInstruction badInst = getPseudoInstruction(instrMemBuffer,
instrMemBuffer, contextValue, block); prototype, contextValue, block);
InstructionError.dumpInstructionDifference(badInst, InstructionError.dumpInstructionDifference(badInst,
existingBlockStartInstr); existingBlockStartInstr);
block.setInconsistentPrototypeConflict(addr, flowFrom); block.setInconsistentPrototypeConflict(addr, flowFrom);
} }
blockTerminated(block);
return; return;
} }
// existing block must be an empty conflicted block - just keep going // existing block must be an empty conflicted block - just keep going
@ -990,16 +1002,18 @@ public class Disassembler implements DisassemblerConflictHandler {
disassemblerQueue disassemblerQueue
.queueDelaySlotFallthrough(existingBlockStartInstr); .queueDelaySlotFallthrough(existingBlockStartInstr);
} }
blockTerminated(block);
return; return;
} }
} }
else if (existingProto.equals(prototype)) { else if (existingProto.equals(prototype)) {
// skip block start silently if it was previously disassembled // skip block start silently if it was previously disassembled
blockTerminated(block);
return; return;
} }
PseudoInstruction badInst = getPseudoInstruction(addr, prototype, PseudoInstruction badInst =
instrMemBuffer, contextValue, block); getPseudoInstruction(instrMemBuffer, prototype, contextValue, block);
InstructionError.dumpInstructionDifference(badInst, InstructionError.dumpInstructionDifference(badInst,
existingBlockStartInstr); existingBlockStartInstr);
@ -1008,7 +1022,7 @@ public class Disassembler implements DisassemblerConflictHandler {
} }
PseudoInstruction inst = PseudoInstruction inst =
getPseudoInstruction(addr, prototype, instrMemBuffer, contextValue, block); getPseudoInstruction(instrMemBuffer, prototype, contextValue, block);
Address maxAddr = inst.getMaxAddress(); Address maxAddr = inst.getMaxAddress();
if (instructionSet != null && instructionSet.intersects(addr, maxAddr)) { if (instructionSet != null && instructionSet.intersects(addr, maxAddr)) {
@ -1038,15 +1052,15 @@ public class Disassembler implements DisassemblerConflictHandler {
} }
if (!existingProto.equals(prototype)) { if (!existingProto.equals(prototype)) {
PseudoInstruction badInst = getPseudoInstruction(instrMemBuffer,
PseudoInstruction badInst = getPseudoInstruction(addr, prototype, prototype, contextValue, block);
instrMemBuffer, contextValue, block);
InstructionError.dumpInstructionDifference(badInst, InstructionError.dumpInstructionDifference(badInst,
existingBlockStartInstr); existingBlockStartInstr);
block.setInconsistentPrototypeConflict(addr, flowFrom); block.setInconsistentPrototypeConflict(addr, flowFrom);
} }
} }
blockTerminated(block);
return; return;
} }
@ -1059,6 +1073,7 @@ public class Disassembler implements DisassemblerConflictHandler {
addr = processInstruction(inst, blockMemBuffer, block, instructionSet); addr = processInstruction(inst, blockMemBuffer, block, instructionSet);
if (addr == null || block.hasInstructionError()) { if (addr == null || block.hasInstructionError()) {
blockTerminated(block);
return; return;
} }
if (endBlockEarly(inst, addr, limit, block) || endBlockOnCall(inst, addr, block)) { if (endBlockEarly(inst, addr, limit, block) || endBlockOnCall(inst, addr, block)) {
@ -1067,6 +1082,7 @@ public class Disassembler implements DisassemblerConflictHandler {
// are added to facilitate future prioritization of flows // are added to facilitate future prioritization of flows
// block.setFallThrough(addr); // block.setFallThrough(addr);
disassemblerContext.copyToFutureFlowState(addr); disassemblerContext.copyToFutureFlowState(addr);
blockTerminated(block);
return; return;
} }
@ -1076,17 +1092,45 @@ public class Disassembler implements DisassemblerConflictHandler {
catch (AddressOutOfBoundsException | AddressOverflowException e) { catch (AddressOutOfBoundsException | AddressOverflowException e) {
block.setInstructionMemoryError(addr, flowFrom, block.setInstructionMemoryError(addr, flowFrom,
"Instruction does not fit within address space constraint"); "Instruction does not fit within address space constraint");
blockTerminated(block);
} }
catch (InsufficientBytesException e) { catch (InsufficientBytesException e) {
block.setInstructionMemoryError(addr, flowFrom, e.getMessage()); block.setInstructionMemoryError(addr, flowFrom, e.getMessage());
blockTerminated(block);
} }
catch (UnknownInstructionException e) { catch (UnknownInstructionException e) {
block.setParseConflict(addr, block.setParseConflict(addr,
disassemblerContext.getRegisterValue(disassemblerContext.getBaseContextRegister()), disassemblerContext.getRegisterValue(disassemblerContext.getBaseContextRegister()),
flowFrom, e.getMessage()); flowFrom, e.getMessage());
blockTerminated(block);
} }
} }
/**
* Signal that block disassembly has been terminated. An error condition may
* have been recorded within the block (see {@link InstructionBlock#hasInstructionError()}
* or block terminated early due to a matching instruction within the active instruction
* set had already been disassembled.
* @param block instruction block
*/
protected void blockTerminated(InstructionBlock block) {
// do nothing - intended for use by extension to cleanup state
}
/**
* Perform parse of instruction bytes and context to produce an instruction prototype.
* @param instrMemBuffer memory buffer
* @param block fallthrough sequence of instructions preceeding current instruction
* required to facilitate potential crossbuilds for current instruction.
* @return instruction prototype
* @throws InsufficientBytesException
* @throws UnknownInstructionException
*/
protected InstructionPrototype parseInstructionPrototype(MemBuffer instrMemBuffer,
InstructionBlock block) throws InsufficientBytesException, UnknownInstructionException {
return language.parse(instrMemBuffer, disassemblerContext, false);
}
private boolean endBlockEarly(Instruction inst, Address fallThruAddr, int limit, private boolean endBlockEarly(Instruction inst, Address fallThruAddr, int limit,
InstructionBlock block) { InstructionBlock block) {
if (fallThruAddr == null) { if (fallThruAddr == null) {
@ -1131,34 +1175,51 @@ public class Disassembler implements DisassemblerConflictHandler {
return true; return true;
} }
/** private PseudoInstruction getPseudoInstruction(MemBuffer memBuffer,
* Adjust disassembler context prior to disassembly of a new instruction. InstructionPrototype prototype, RegisterValue contextValue, InstructionBlock block)
* @param instrMemBuffer buffer for bytes from memory
* @throws UnknownInstructionException if instruction is invalid
*/
protected void adjustPreParseContext(MemBuffer instrMemBuffer)
throws UnknownInstructionException {
// nothing to do - method provided for disassembler extensions
}
protected PseudoInstruction getPseudoInstruction(Address addr, InstructionPrototype prototype,
MemBuffer memBuffer, RegisterValue contextValue, InstructionBlock block)
throws AddressOverflowException { throws AddressOverflowException {
Address addr = memBuffer.getAddress();
ProcessorContext processorContext =
getProcessorContext(addr, prototype.getLength(), contextValue);
PseudoInstruction instr; PseudoInstruction instr;
if (program != null) { if (program != null) {
instr = new PseudoInstruction(program, addr, prototype, memBuffer, instr = new PseudoInstruction(program, addr, prototype, memBuffer, processorContext);
disassemblerProgramContext.getInstructionContext(contextValue, addr,
prototype.getLength()));
} }
else { else {
instr = new PseudoInstruction(addrFactory, addr, prototype, memBuffer, instr =
disassemblerProgramContext.getInstructionContext(contextValue, addr, new PseudoInstruction(addrFactory, addr, prototype, memBuffer, processorContext);
prototype.getLength()));
} }
instr.setInstructionBlock(block); instr.setInstructionBlock(block);
return instr; return instr;
} }
/**
* Get the processor context for the current instruction being disassembled as reflected by
* the {@link #disassemblerContext}. This is intended to be used in forming a
* {@link PseudoInstruction} or exercising certain methods on an {@link InstructionPrototype}.
*
* @param addr current instruction address
* @param instrLength instruction byte length
* @param contextValue instruction context register value
* @return processor context
*/
protected ProcessorContext getProcessorContext(Address addr, int instrLength,
RegisterValue contextValue) {
return disassemblerProgramContext.getInstructionContext(contextValue, addr, instrLength);
}
/**
* Determine if {@link InstructionBlock} termination is allowed after the specified instruction
* has been added. Based upon the crossbuild requirements / parallel instruction semantics,
* this method may return false to force continued fallthrough instruction accumulation within
* the current block.
*
* @param instr last instruction disassembled
* @return true if current block with last instruction may be terminated, false if block
* must continue fallthrough instruction accumulation.
*/
protected boolean isBlockTerminationOK(Instruction instr) { protected boolean isBlockTerminationOK(Instruction instr) {
return parallelHelper == null || parallelHelper.isEndOfParallelInstructionGroup(instr); return parallelHelper == null || parallelHelper.isEndOfParallelInstructionGroup(instr);
} }
@ -1167,6 +1228,7 @@ public class Disassembler implements DisassemblerConflictHandler {
* Process a new instruction which has just been parsed. This method is responsible for * Process a new instruction which has just been parsed. This method is responsible for
* adding the instruction to the current block as well as any delay-slotted instructions. * adding the instruction to the current block as well as any delay-slotted instructions.
* This method may be overridden and the instruction re-parsed if necessary. * This method may be overridden and the instruction re-parsed if necessary.
*
* @param inst instruction to process * @param inst instruction to process
* @param blockMemBuffer buffer to get bytes * @param blockMemBuffer buffer to get bytes
* @param block current block of instructions * @param block current block of instructions
@ -1317,7 +1379,7 @@ public class Disassembler implements DisassemblerConflictHandler {
} }
PseudoInstruction dsInstr = PseudoInstruction dsInstr =
getPseudoInstruction(addr, prototype, dsInstrMemBuffer, contextValue, block); getPseudoInstruction(dsInstrMemBuffer, prototype, contextValue, block);
if (repeatInstructionByteTracker.exceedsRepeatBytePattern(dsInstr)) { if (repeatInstructionByteTracker.exceedsRepeatBytePattern(dsInstr)) {
block.setParseConflict(addr, contextValue, instAddr, block.setParseConflict(addr, contextValue, instAddr,
@ -1389,11 +1451,13 @@ public class Disassembler implements DisassemblerConflictHandler {
RegisterValue contextValue = conflict.getParseContextValue(); RegisterValue contextValue = conflict.getParseContextValue();
if (contextValue != null) { if (contextValue != null) {
try { try {
RegisterValue curContextValue = program.getProgramContext().getRegisterValue(contextValue.getRegister(), address); RegisterValue curContextValue = program.getProgramContext()
.getRegisterValue(contextValue.getRegister(), address);
// only store if different than what is already there, which could be a default value // only store if different than what is already there, which could be a default value
if (!contextValue.equals(curContextValue)) { if (!contextValue.equals(curContextValue)) {
program.getProgramContext().setRegisterValue(address, address, contextValue); program.getProgramContext()
.setRegisterValue(address, address, contextValue);
} }
} }
catch (ContextChangeException e) { catch (ContextChangeException e) {
@ -1554,7 +1618,7 @@ public class Disassembler implements DisassemblerConflictHandler {
* @param instrLength length of instruction to set context * @param instrLength length of instruction to set context
* @return instruction context with possible added value * @return instruction context with possible added value
*/ */
ProcessorContext getInstructionContext(RegisterValue value, Address instrAddr, protected ProcessorContext getInstructionContext(RegisterValue value, Address instrAddr,
int instrLength) { int instrLength) {
if (value == null) { if (value == null) {