mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-27 20:00:29 +08:00
Merge remote-tracking branch 'origin/GP-6496_emteere_RefactorPsuedoDisassembler' into patch
This commit is contained in:
+126
-109
@@ -19,6 +19,7 @@ import java.math.BigInteger;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import ghidra.program.disassemble.Disassembler;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.data.PointerDataType;
|
import ghidra.program.model.data.PointerDataType;
|
||||||
@@ -28,6 +29,7 @@ import ghidra.program.model.mem.*;
|
|||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PseudoDisassembler.java
|
* PseudoDisassembler.java
|
||||||
@@ -71,7 +73,7 @@ public class PseudoDisassembler {
|
|||||||
|
|
||||||
private boolean respectExecuteFlag = false;
|
private boolean respectExecuteFlag = false;
|
||||||
|
|
||||||
private int lastCheckValidDisassemblyCount; // number of last instructions disassembled
|
private int lastCheckValidDisassemblyCount; // number of last instructions disassembled in checkValidSubroutine
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a pseudo disassembler for the given program.
|
* Create a pseudo disassembler for the given program.
|
||||||
@@ -98,8 +100,11 @@ public class PseudoDisassembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the last number of disassembled instructions
|
* Get the last number of disassembled instructions,
|
||||||
* or the number of initial contiguous instruction if requireContiguous is true
|
* or the number of initial contiguous instruction if requireContiguous is true,
|
||||||
|
* after calls to in checkValidSubroutine() or isValidSubroutine()
|
||||||
|
*
|
||||||
|
* @return number of disassembled instructions
|
||||||
*/
|
*/
|
||||||
public int getLastCheckValidInstructionCount() {
|
public int getLastCheckValidInstructionCount() {
|
||||||
return lastCheckValidDisassemblyCount;
|
return lastCheckValidDisassemblyCount;
|
||||||
@@ -128,17 +133,15 @@ public class PseudoDisassembler {
|
|||||||
UnknownInstructionException, UnknownContextException {
|
UnknownInstructionException, UnknownContextException {
|
||||||
|
|
||||||
PseudoDisassemblerContext procContext = new PseudoDisassemblerContext(programContext);
|
PseudoDisassemblerContext procContext = new PseudoDisassemblerContext(programContext);
|
||||||
|
|
||||||
procContext.flowStart(addr);
|
|
||||||
return disassemble(addr, procContext, false);
|
return disassemble(addr, procContext, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disassemble a single instruction. The program is not affected.
|
* Disassemble a single instruction. The program is not affected.
|
||||||
* @param addr
|
* @param addr location to disassemble
|
||||||
* @param disassemblerContext
|
* @param disassemblerContext context for disassembly
|
||||||
* @param isInDelaySlot
|
* @param isInDelaySlot true if instruction to be disassembled is in a delaySlot
|
||||||
* @return
|
* @return disassembled instruction
|
||||||
* @throws InsufficientBytesException
|
* @throws InsufficientBytesException
|
||||||
* @throws UnknownInstructionException
|
* @throws UnknownInstructionException
|
||||||
* @throws UnknownContextException
|
* @throws UnknownContextException
|
||||||
@@ -148,41 +151,18 @@ public class PseudoDisassembler {
|
|||||||
throws InsufficientBytesException, UnknownInstructionException,
|
throws InsufficientBytesException, UnknownInstructionException,
|
||||||
UnknownContextException {
|
UnknownContextException {
|
||||||
|
|
||||||
MemBuffer memBuffer = new DumbMemBufferImpl(memory, addr);
|
addr = setTargetContextForDisassembly(disassemblerContext, addr);
|
||||||
|
RegisterValue entryContext = getTargetStartContext(addr, disassemblerContext);
|
||||||
// check that address is defined in memory
|
|
||||||
try {
|
|
||||||
memBuffer.getByte(0);
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionPrototype prototype = null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
prototype = language.parse(memBuffer, disassemblerContext, isInDelaySlot);
|
// Only disassemble one instruction, and always re-disassemble
|
||||||
|
// in case context has changed.
|
||||||
|
Instruction instr = pseudoDisassemble(addr, entryContext, 1, true);
|
||||||
|
return (PseudoInstruction) instr;
|
||||||
}
|
}
|
||||||
catch (UnknownInstructionException unknownExc) {
|
catch (UnknownInstructionException unknownExc) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prototype == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
PseudoInstruction instr;
|
|
||||||
try {
|
|
||||||
instr = new PseudoInstruction(program, addr, prototype, memBuffer, disassemblerContext);
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
// this is here, if a prototype matches for some number of bytes, but
|
|
||||||
// the actual instruction is longer than the number of bytes needed for matching
|
|
||||||
// the prototype. And all the bytes for the instruction are not available.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return instr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -224,34 +204,21 @@ public class PseudoDisassembler {
|
|||||||
PseudoDisassemblerContext disassemblerContext) throws InsufficientBytesException,
|
PseudoDisassemblerContext disassemblerContext) throws InsufficientBytesException,
|
||||||
UnknownInstructionException, UnknownContextException {
|
UnknownInstructionException, UnknownContextException {
|
||||||
|
|
||||||
|
addr = setTargetContextForDisassembly(disassemblerContext, addr);
|
||||||
|
RegisterValue targetContext = getTargetStartContext(addr, disassemblerContext);
|
||||||
|
|
||||||
|
pseudoDisassembler = Disassembler.getDisassembler(program, false, false, false,
|
||||||
|
TaskMonitor.DUMMY, msg -> {
|
||||||
|
// ignore log errors
|
||||||
|
});
|
||||||
|
|
||||||
MemBuffer memBuffer = new ByteMemBufferImpl(addr, bytes, language.isBigEndian());
|
MemBuffer memBuffer = new ByteMemBufferImpl(addr, bytes, language.isBigEndian());
|
||||||
|
InstructionBlock instrBlock = pseudoDisassembler.pseudoDisassembleBlock(memBuffer, targetContext, 1);
|
||||||
// check that address is defined in memory
|
if (instrBlock == null) {
|
||||||
try {
|
|
||||||
memBuffer.getByte(0);
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstructionPrototype prototype = null;
|
return (PseudoInstruction) instrBlock.getInstructionAt(addr);
|
||||||
disassemblerContext.flowStart(addr);
|
|
||||||
prototype = language.parse(memBuffer, disassemblerContext, false);
|
|
||||||
|
|
||||||
if (prototype == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
PseudoInstruction instr;
|
|
||||||
try {
|
|
||||||
instr = new PseudoInstruction(program, addr, prototype, memBuffer, disassemblerContext);
|
|
||||||
}
|
|
||||||
catch (AddressOverflowException e) {
|
|
||||||
throw new InsufficientBytesException(
|
|
||||||
"failed to build pseudo instruction at " + addr + ": " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return instr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,9 +232,7 @@ public class PseudoDisassembler {
|
|||||||
* @return {@link PseudoData} that acts like Data
|
* @return {@link PseudoData} that acts like Data
|
||||||
*/
|
*/
|
||||||
public PseudoData applyDataType(Address addr, DataType dt) {
|
public PseudoData applyDataType(Address addr, DataType dt) {
|
||||||
|
|
||||||
Memory memory = program.getMemory();
|
|
||||||
|
|
||||||
MemBuffer memBuffer = new DumbMemBufferImpl(memory, addr);
|
MemBuffer memBuffer = new DumbMemBufferImpl(memory, addr);
|
||||||
|
|
||||||
// check that address is defined in memory
|
// check that address is defined in memory
|
||||||
@@ -424,9 +389,10 @@ public class PseudoDisassembler {
|
|||||||
AddressSet body = new AddressSet();
|
AddressSet body = new AddressSet();
|
||||||
AddressSet instrStarts = new AddressSet();
|
AddressSet instrStarts = new AddressSet();
|
||||||
|
|
||||||
entryPoint = setTargetContextForDisassembly(procContext, entryPoint);
|
|
||||||
|
|
||||||
Address target = entryPoint;
|
Address target = entryPoint;
|
||||||
|
|
||||||
|
entryPoint = setTargetContextForDisassembly(procContext, entryPoint);
|
||||||
|
RegisterValue entryContext = getTargetStartContext(entryPoint, procContext);
|
||||||
|
|
||||||
ArrayList<Address> targetList = new ArrayList<>(); // list of valid targets
|
ArrayList<Address> targetList = new ArrayList<>(); // list of valid targets
|
||||||
ArrayList<Address> untriedTargetList = new ArrayList<>(); // list of valid targets
|
ArrayList<Address> untriedTargetList = new ArrayList<>(); // list of valid targets
|
||||||
@@ -458,17 +424,15 @@ public class PseudoDisassembler {
|
|||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
procContext.flowStart(entryPoint);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// look some number of fallthroughs to see if this
|
// look some number of fallthroughs to see if this
|
||||||
// is a valid run of instructions.
|
// is a valid run of instructions.
|
||||||
|
|
||||||
for (int i = 0; target != null && i < maxInstr; i++) {
|
for (int i = 0; target != null && i < maxInstr; i++) {
|
||||||
PseudoInstruction instr;
|
Instruction instr;
|
||||||
instr = disassemble(target, procContext, false);
|
instr = pseudoDisassemble(target, entryContext, maxInstructions, false);
|
||||||
|
|
||||||
boolean doContinue = processor.process(instr);
|
boolean doContinue = processor.process((PseudoInstruction)instr);
|
||||||
if (!doContinue) {
|
if (!doContinue) {
|
||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
@@ -482,7 +446,7 @@ public class PseudoDisassembler {
|
|||||||
instrStarts.addRange(instr.getMinAddress(), instr.getMinAddress());
|
instrStarts.addRange(instr.getMinAddress(), instr.getMinAddress());
|
||||||
|
|
||||||
// check whether processor wants to follow flow on this instruction
|
// check whether processor wants to follow flow on this instruction
|
||||||
if (!processor.followFlows(instr)) {
|
if (!processor.followFlows((PseudoInstruction) instr)) {
|
||||||
target = getNextTarget(body, untriedTargetList);
|
target = getNextTarget(body, untriedTargetList);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -530,13 +494,8 @@ public class PseudoDisassembler {
|
|||||||
target = newTarget;
|
target = newTarget;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InsufficientBytesException e) {
|
|
||||||
processor.process(null);
|
|
||||||
}
|
|
||||||
catch (UnknownInstructionException e) {
|
catch (UnknownInstructionException e) {
|
||||||
processor.process(null);
|
// bad instruction
|
||||||
}
|
|
||||||
catch (UnknownContextException e) {
|
|
||||||
processor.process(null);
|
processor.process(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,6 +621,7 @@ public class PseudoDisassembler {
|
|||||||
AddressSetView execSet = memory.getExecuteSet();
|
AddressSetView execSet = memory.getExecuteSet();
|
||||||
|
|
||||||
entryPoint = setTargetContextForDisassembly(procContext, entryPoint);
|
entryPoint = setTargetContextForDisassembly(procContext, entryPoint);
|
||||||
|
RegisterValue entryContext = getTargetStartContext(entryPoint, procContext);
|
||||||
|
|
||||||
Address target = entryPoint;
|
Address target = entryPoint;
|
||||||
|
|
||||||
@@ -683,24 +643,12 @@ public class PseudoDisassembler {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RepeatInstructionByteTracker repeatInstructionByteTracker =
|
|
||||||
new RepeatInstructionByteTracker(MAX_REPEAT_BYTES_LIMIT, null);
|
|
||||||
|
|
||||||
procContext.flowStart(entryPoint);
|
|
||||||
try {
|
try {
|
||||||
// look some number of fallthroughs to see if this
|
// look some number of fallthroughs to see if this
|
||||||
// is a valid run of instructions.
|
// is a valid run of instructions.
|
||||||
|
|
||||||
for (int i = 0; target != null && i < maxInstructions; i++) {
|
for (int i = 0; target != null && i < maxInstructions; i++) {
|
||||||
if (target.compareTo(procContext.getAddress()) < 0) {
|
Instruction instr = pseudoDisassemble(target, entryContext, maxInstructions, false);
|
||||||
procContext.copyToFutureFlowState(target);
|
|
||||||
procContext.flowEnd(procContext.getAddress());
|
|
||||||
procContext.flowStart(target);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
procContext.flowToAddress(target);
|
|
||||||
}
|
|
||||||
PseudoInstruction instr = disassemble(target, procContext, false);
|
|
||||||
|
|
||||||
if (instr == null) {
|
if (instr == null) {
|
||||||
// if the target is in the external section, which is uninitialized, ignore it!
|
// if the target is in the external section, which is uninitialized, ignore it!
|
||||||
@@ -712,7 +660,6 @@ public class PseudoDisassembler {
|
|||||||
}
|
}
|
||||||
targetList.remove(target);
|
targetList.remove(target);
|
||||||
target = getNextTarget(body, untriedTargetList);
|
target = getNextTarget(body, untriedTargetList);
|
||||||
repeatInstructionByteTracker.reset();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -723,7 +670,7 @@ public class PseudoDisassembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if we are getting into bad instruction runs
|
// check if we are getting into bad instruction runs
|
||||||
if (repeatInstructionByteTracker.exceedsRepeatBytePattern(instr)) {
|
if (lastPseudoInstructionBlock.getInstructionConflict() != null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -743,8 +690,7 @@ public class PseudoDisassembler {
|
|||||||
catch (AddressOverflowException e) {
|
catch (AddressOverflowException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
procContext.flowToAddress(addr);
|
Instruction dsInstr = pseudoDisassemble(addr, entryContext, maxInstructions, false);
|
||||||
PseudoInstruction dsInstr = disassemble(addr, procContext, true);
|
|
||||||
if (dsInstr == null) {
|
if (dsInstr == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -762,9 +708,8 @@ public class PseudoDisassembler {
|
|||||||
// if instruction has fall thru
|
// if instruction has fall thru
|
||||||
Address fallThru = null;
|
Address fallThru = null;
|
||||||
if (instr.hasFallthrough()) {
|
if (instr.hasFallthrough()) {
|
||||||
if (checkNonReturning(program, flowType, instr)) {
|
if (checkNonReturning(flowType, instr)) {
|
||||||
target = getNextTarget(body, untriedTargetList);
|
target = getNextTarget(body, untriedTargetList);
|
||||||
repeatInstructionByteTracker.reset();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
newTarget = instr.getFallThrough();
|
newTarget = instr.getFallThrough();
|
||||||
@@ -793,7 +738,6 @@ public class PseudoDisassembler {
|
|||||||
|
|
||||||
if (newTarget == null) {
|
if (newTarget == null) {
|
||||||
newTarget = getNextTarget(body, untriedTargetList);
|
newTarget = getNextTarget(body, untriedTargetList);
|
||||||
repeatInstructionByteTracker.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -817,7 +761,6 @@ public class PseudoDisassembler {
|
|||||||
if (func != null) {
|
if (func != null) {
|
||||||
didCallValidSubroutine = true;
|
didCallValidSubroutine = true;
|
||||||
newTarget = getNextTarget(body, untriedTargetList);
|
newTarget = getNextTarget(body, untriedTargetList);
|
||||||
repeatInstructionByteTracker.reset();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
targetList.add(address);
|
targetList.add(address);
|
||||||
@@ -865,18 +808,10 @@ public class PseudoDisassembler {
|
|||||||
target = newTarget;
|
target = newTarget;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InsufficientBytesException e) {
|
|
||||||
// can't parse not enough bytes
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (UnknownInstructionException e) {
|
catch (UnknownInstructionException e) {
|
||||||
// bad instruction
|
// bad instruction
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch (UnknownContextException e) {
|
|
||||||
// something wrong with context
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get rid of anything on target list that is in body of instruction
|
// get rid of anything on target list that is in body of instruction
|
||||||
Iterator<Address> iter = targetList.iterator();
|
Iterator<Address> iter = targetList.iterator();
|
||||||
@@ -905,8 +840,71 @@ public class PseudoDisassembler {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private InstructionBlock lastPseudoInstructionBlock;
|
||||||
|
private Disassembler pseudoDisassembler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disassemble a block of instructions
|
||||||
|
*
|
||||||
|
* @param addr location to disassemble
|
||||||
|
* @param entryContext context at the start of the block
|
||||||
|
* @param maxInstr maximum number of instructions to disassemble
|
||||||
|
* @param forceRedisassembly force re-disassembly (don't use cached block)
|
||||||
|
*
|
||||||
|
* @return the instruction (PseudoInstruction)
|
||||||
|
* @throws UnknownInstructionException if instruction at location not disassemblable
|
||||||
|
*/
|
||||||
|
|
||||||
private boolean checkNonReturning(Program program, FlowType flowType, PseudoInstruction instr) {
|
private Instruction pseudoDisassemble(Address addr, RegisterValue entryContext, int maxInstr, boolean forceRedisassembly) throws UnknownInstructionException {
|
||||||
|
Instruction instr;
|
||||||
|
if (!forceRedisassembly && lastPseudoInstructionBlock != null) {
|
||||||
|
instr = lastPseudoInstructionBlock.getInstructionAt(addr);
|
||||||
|
if (instr != null) {
|
||||||
|
return instr;
|
||||||
|
}
|
||||||
|
InstructionError error = lastPseudoInstructionBlock.getInstructionConflict();
|
||||||
|
if (error != null && addr.equals(error.getInstructionAddress())) {
|
||||||
|
throw new UnknownInstructionException(error.getConflictMessage());
|
||||||
|
}
|
||||||
|
lastPseudoInstructionBlock = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pseudoDisassembler == null) {
|
||||||
|
pseudoDisassembler = Disassembler.getDisassembler(program, false, false, false,
|
||||||
|
TaskMonitor.DUMMY, msg -> {
|
||||||
|
// ignore log errors
|
||||||
|
});
|
||||||
|
pseudoDisassembler.setRepeatPatternLimit(MAX_REPEAT_BYTES_LIMIT);
|
||||||
|
} else if (forceRedisassembly) {
|
||||||
|
pseudoDisassembler.resetDisassemblerContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPseudoInstructionBlock =
|
||||||
|
pseudoDisassembler.pseudoDisassembleBlock(addr, entryContext, maxInstr);
|
||||||
|
if (lastPseudoInstructionBlock != null) {
|
||||||
|
InstructionError error = lastPseudoInstructionBlock.getInstructionConflict(); // Look for zero-byte run first
|
||||||
|
if (error != null &&
|
||||||
|
error.getConflictMessage().startsWith("Maximum run of Zero-Byte")) {
|
||||||
|
throw new UnknownInstructionException(error.getConflictMessage()); // Don't return any of the zero-byte instructions
|
||||||
|
}
|
||||||
|
instr = lastPseudoInstructionBlock.getInstructionAt(addr);
|
||||||
|
if (instr != null) {
|
||||||
|
return instr;
|
||||||
|
}
|
||||||
|
if (error != null && addr.equals(error.getInstructionAddress())) {
|
||||||
|
throw new UnknownInstructionException(error.getConflictMessage());
|
||||||
|
}
|
||||||
|
if (program.getMemory().isExternalBlockAddress(addr)) {
|
||||||
|
throw new UnknownInstructionException(
|
||||||
|
"Unable to disassemble EXTERNAL block location: " + addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new UnknownInstructionException("Invalid instruction address (improperly aligned)");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkNonReturning(FlowType flowType, Instruction instr) {
|
||||||
if (!flowType.isCall()) {
|
if (!flowType.isCall()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1059,6 +1057,25 @@ public class PseudoDisassembler {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the context register setup with the starting context for target
|
||||||
|
*
|
||||||
|
* @param target address to get context register value
|
||||||
|
* @param procContext disassembly context
|
||||||
|
* @return context register value set with current context for target
|
||||||
|
*/
|
||||||
|
private RegisterValue getTargetStartContext(Address target,
|
||||||
|
PseudoDisassemblerContext procContext) {
|
||||||
|
procContext.flowStart(target);
|
||||||
|
|
||||||
|
RegisterValue entryContext = null;
|
||||||
|
Register baseContextRegister = procContext.getBaseContextRegister();
|
||||||
|
if (baseContextRegister != null) {
|
||||||
|
entryContext = procContext.getRegisterValue(baseContextRegister);
|
||||||
|
}
|
||||||
|
return entryContext;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if program has uses the low bit of an address to change Instruction Set mode
|
* @return true if program has uses the low bit of an address to change Instruction Set mode
|
||||||
|
|||||||
Reference in New Issue
Block a user