mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
Candidate release of source code.
This commit is contained in:
parent
db81e6b3b0
commit
79d8f164f8
12449 changed files with 2800756 additions and 16 deletions
|
@ -0,0 +1,110 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.analysis;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a temporary analyzer, until we can get the pattern search framework up and going.
|
||||
* This searches for patterns that are functions that have side-effects.
|
||||
*/
|
||||
|
||||
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
||||
import ghidra.app.cmd.function.CreateFunctionCmd;
|
||||
import ghidra.app.plugin.core.searchmem.RegExSearchData;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Processor;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.datastruct.ListAccumulator;
|
||||
import ghidra.util.search.memory.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class ARMPreAnalyzer extends AbstractAnalyzer {
|
||||
private static String DESCRIPTION =
|
||||
"Analyze ARM binaries for switch8_r3 functions. This will be replaced by a general hashing algorithm next release.";
|
||||
|
||||
public ARMPreAnalyzer() {
|
||||
super("ARM Pre-Pattern Analyzer", DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
|
||||
setPriority(AnalysisPriority.BLOCK_ANALYSIS.after());
|
||||
setDefaultEnablement(true);
|
||||
setSupportsOneTimeAnalysis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAnalyze(Program program) {
|
||||
Processor processor = program.getLanguage().getProcessor();
|
||||
return (processor.equals(Processor.findOrPossiblyCreateProcessor("ARM")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) {
|
||||
|
||||
String switch_fn = "\\x01\\xc0\\x5e\\xe5" + // ldrb ip,[lr,#-0x1]
|
||||
"\\x0c\\x00\\x53\\xe1" + // cmp r3,ip
|
||||
"(" + "\\x03\\x30\\xde\\x37" + // ldrbcc r3,[lr,r3]
|
||||
"\\x0c\\x30\\xde\\x27" + // ldrbcs r3,[lr,ip]
|
||||
"|" + // OR
|
||||
"\\x0c\\x30\\xde\\x27" + // ldrbcs r3,[lr,ip]
|
||||
"\\x03\\x30\\xde\\x37" + // ldrbcc r3,[lr,r3]
|
||||
")" + "(" + "\\x83\\xc0\\x8e\\xe0" + // add ip,lr,r3, lsl #0x1
|
||||
"\\x1c\\xff\\x2f\\xe1" + // bx ip
|
||||
"|" + // OR
|
||||
"\\x83\\xe0\\x8e\\xe0" + // add lr,lr,r3, lsl #0x1
|
||||
"\\x1e\\xff\\x2f\\xe1" + // bx lr
|
||||
")";
|
||||
|
||||
RegExSearchData searchData = RegExSearchData.createRegExSearchData(switch_fn);
|
||||
|
||||
SearchInfo searchInfo = new SearchInfo(searchData, 30, false, true, 4, false, null);
|
||||
|
||||
AddressSet intersection =
|
||||
program.getMemory().getLoadedAndInitializedAddressSet().intersect(set);
|
||||
RegExMemSearcherAlgorithm searcher =
|
||||
new RegExMemSearcherAlgorithm(searchInfo, intersection, program, true);
|
||||
|
||||
ListAccumulator<MemSearchResult> accumulator = new ListAccumulator<>();
|
||||
searcher.search(accumulator, monitor);
|
||||
List<MemSearchResult> results = accumulator.asList();
|
||||
|
||||
// create a function here with the correct call fixup
|
||||
for (MemSearchResult result : results) {
|
||||
|
||||
Address addr = result.getAddress();
|
||||
|
||||
// disassemble ARM
|
||||
DisassembleCommand disassembleCommand = new DisassembleCommand(addr, null, true);
|
||||
disassembleCommand.applyTo(program);
|
||||
|
||||
// create function
|
||||
CreateFunctionCmd createFunctionCmd = new CreateFunctionCmd(addr, false);
|
||||
createFunctionCmd.applyTo(program);
|
||||
|
||||
// set call fixup
|
||||
Function func = program.getFunctionManager().getFunctionAt(addr);
|
||||
if (func != null) {
|
||||
func.setCallFixup("switch8_r3");
|
||||
}
|
||||
|
||||
BookmarkManager bookmarkManager = program.getBookmarkManager();
|
||||
bookmarkManager.setBookmark(addr, BookmarkType.ANALYSIS, getName(),
|
||||
"Found Switch8_r3 Function");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,831 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.analysis;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import ghidra.app.cmd.function.CreateFunctionCmd;
|
||||
import ghidra.app.cmd.label.AddLabelCmd;
|
||||
import ghidra.app.plugin.core.disassembler.AddressTable;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.disassemble.Disassembler;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.block.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.program.util.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class ArmAnalyzer extends ConstantPropagationAnalyzer {
|
||||
private static final String SWITCH_OPTION_NAME = "Switch Table Recovery";
|
||||
private static final String SWITCH_OPTION_DESCRIPTION = "Turn on to recover switch tables";
|
||||
private static final boolean SWITCH_OPTION_DEFAULT_VALUE = false;
|
||||
|
||||
private boolean recoverSwitchTables = SWITCH_OPTION_DEFAULT_VALUE;
|
||||
|
||||
private static final long MAX_DISTANCE = (4 * 1024);
|
||||
|
||||
private Register tbRegister;
|
||||
private Register tmodeRegister;
|
||||
private Register lrRegister;
|
||||
|
||||
private final static String PROCESSOR_NAME = "ARM";
|
||||
|
||||
public ArmAnalyzer() {
|
||||
super(PROCESSOR_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAnalyze(Program program) {
|
||||
boolean canAnalyze = program.getLanguage().getProcessor().equals(
|
||||
Processor.findOrPossiblyCreateProcessor(PROCESSOR_NAME));
|
||||
|
||||
if (!canAnalyze) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tmodeRegister = program.getProgramContext().getRegister("TMode");
|
||||
tbRegister = program.getProgramContext().getRegister("ISAModeSwitch");
|
||||
lrRegister = program.getProgramContext().getRegister("lr");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet flowConstants(final Program program, Address flowStart,
|
||||
AddressSetView flowSet, final SymbolicPropogator symEval, final TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
// follow all flows building up context
|
||||
// use context to fill out addresses on certain instructions
|
||||
ConstantPropagationContextEvaluator eval =
|
||||
new ConstantPropagationContextEvaluator(trustWriteMemOption) {
|
||||
|
||||
@Override
|
||||
public boolean evaluateContext(VarnodeContext context, Instruction instr) {
|
||||
|
||||
FlowType ftype = instr.getFlowType();
|
||||
if (ftype.isComputed() && ftype.isJump()) {
|
||||
Varnode pcVal = context.getRegisterVarnodeValue(
|
||||
program.getLanguage().getProgramCounter());
|
||||
if (pcVal != null) {
|
||||
if (isLinkRegister(context, pcVal) &&
|
||||
!instr.getFlowType().isTerminal()) {
|
||||
// need to set the return override
|
||||
instr.setFlowOverride(FlowOverride.RETURN);
|
||||
}
|
||||
}
|
||||
// if LR is a constant and is set right after this, this is a call
|
||||
Varnode lrVal = context.getRegisterVarnodeValue(lrRegister);
|
||||
if (lrVal != null) {
|
||||
if (lrVal.isConstant()) {
|
||||
long target = lrVal.getAddress().getOffset();
|
||||
Address addr = instr.getMaxAddress().add(1);
|
||||
if (target == addr.getOffset() && !instr.getFlowType().isCall()) {
|
||||
// if there are is a read reference there as well,
|
||||
// then this is really a branch, not a call
|
||||
if (hasDataReferenceTo(program, addr)) {
|
||||
return false;
|
||||
}
|
||||
instr.setFlowOverride(FlowOverride.CALL);
|
||||
// need to trigger disassembly below! if not already
|
||||
doArmThumbDisassembly(program, instr, context, addr,
|
||||
instr.getFlowType(), false, monitor);
|
||||
// need to trigger re-function creation!
|
||||
Function f = program.getFunctionManager().getFunctionContaining(
|
||||
instr.getMinAddress());
|
||||
if (f != null) {
|
||||
try {
|
||||
CreateFunctionCmd.fixupFunctionBody(program, f,
|
||||
monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return true;
|
||||
}
|
||||
//AutoAnalysisManager.getAnalysisManager(program).functionDefined(
|
||||
// func.getBody());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are any data references to this location.
|
||||
* @param program
|
||||
* @param addr
|
||||
* @return true if there are any data references to addr
|
||||
*/
|
||||
private boolean hasDataReferenceTo(Program program, Address addr) {
|
||||
ReferenceManager refMgr = program.getReferenceManager();
|
||||
if (!refMgr.hasReferencesTo(addr)) {
|
||||
return false;
|
||||
}
|
||||
ReferenceIterator referencesTo = refMgr.getReferencesTo(addr);
|
||||
while (referencesTo.hasNext()) {
|
||||
Reference reference = referencesTo.next();
|
||||
if (reference.getReferenceType().isData()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isLinkRegister(VarnodeContext context, Varnode pcVal) {
|
||||
return (pcVal.isRegister() &&
|
||||
pcVal.getAddress().equals(lrRegister.getAddress())) ||
|
||||
(context.isSymbol(pcVal) &&
|
||||
pcVal.getAddress().getAddressSpace().getName().equals(
|
||||
lrRegister.getName()) &&
|
||||
pcVal.getOffset() == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluateReference(VarnodeContext context, Instruction instr,
|
||||
int pcodeop, Address address, int size, RefType refType) {
|
||||
if (refType.isJump() && refType.isComputed() &&
|
||||
program.getMemory().contains(address) && address.getOffset() != 0) {
|
||||
if (instr.getMnemonicString().startsWith("tb")) {
|
||||
return false;
|
||||
}
|
||||
doArmThumbDisassembly(program, instr, context, address, instr.getFlowType(),
|
||||
true, monitor);
|
||||
return !symEval.encounteredBranch();
|
||||
}
|
||||
if (refType.isData() && program.getMemory().contains(address)) {
|
||||
if (refType.isRead() || refType.isWrite()) {
|
||||
createData(program, address, size);
|
||||
instr.addOperandReference(instr.getNumOperands() - 1, address, refType,
|
||||
SourceType.ANALYSIS);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (refType.isCall() && refType.isComputed()) {
|
||||
// must disassemble right now, because TB flag could get set back at end of blx
|
||||
doArmThumbDisassembly(program, instr, context, address, instr.getFlowType(),
|
||||
true, monitor);
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.evaluateReference(context, instr, pcodeop, address, size, refType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluateDestination(VarnodeContext context,
|
||||
Instruction instruction) {
|
||||
FlowType flowType = instruction.getFlowType();
|
||||
if (!flowType.isJump()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference[] refs = instruction.getReferencesFrom();
|
||||
if (refs.length <= 0 ||
|
||||
(refs.length == 1 && refs[0].getReferenceType().isData()) ||
|
||||
symEval.encounteredBranch()) {
|
||||
destSet.addRange(instruction.getMinAddress(), instruction.getMinAddress());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
AddressSet resultSet = symEval.flowConstants(flowStart, flowSet, eval, true, monitor);
|
||||
|
||||
if (recoverSwitchTables) {
|
||||
recoverSwitches(program, eval.getDestinationSet(), symEval, monitor);
|
||||
}
|
||||
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
private void recoverSwitches(final Program program, AddressSet destSet,
|
||||
SymbolicPropogator symEval, TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
// now handle symbolic execution assuming values!
|
||||
class SwitchEvaluator implements ContextEvaluator {
|
||||
|
||||
int tableSizeMax = 64;
|
||||
Long assumeValue = new Long(0);
|
||||
Address targetSwitchAddr = null;
|
||||
int addrByteSize = 1;
|
||||
boolean hitTheGuard = false;
|
||||
ArrayList<Address> targetList = new ArrayList<Address>();
|
||||
ArrayList<Address> accessList = new ArrayList<Address>();
|
||||
|
||||
public void init(Address loc, int maxSize) {
|
||||
addrByteSize = 1;
|
||||
assumeValue = new Long(0);
|
||||
tableSizeMax = maxSize;
|
||||
targetSwitchAddr = loc;
|
||||
hitTheGuard = false;
|
||||
|
||||
targetList.clear();
|
||||
accessList.clear();
|
||||
}
|
||||
|
||||
public void initForCase(Long assume) {
|
||||
assumeValue = new Long(assume);
|
||||
hitTheGuard = false;
|
||||
}
|
||||
|
||||
public int getTableSizeMax() {
|
||||
return tableSizeMax;
|
||||
}
|
||||
|
||||
public int getAddrByteSize() {
|
||||
return addrByteSize;
|
||||
}
|
||||
|
||||
public ArrayList<Address> getTargetList() {
|
||||
return targetList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluateContextBefore(VarnodeContext context, Instruction instr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluateContext(VarnodeContext context, Instruction instr) {
|
||||
if (context.readExecutableCode()) {
|
||||
return true;
|
||||
}
|
||||
// find the cmpli to set the size of the table
|
||||
// tableSize = size
|
||||
String mnemonic = instr.getMnemonicString();
|
||||
if ((mnemonic.compareToIgnoreCase("cmp") == 0)) {
|
||||
int numOps = instr.getNumOperands();
|
||||
if (numOps > 1) {
|
||||
Register reg = instr.getRegister(numOps - 2);
|
||||
if ((reg != null)) {
|
||||
context.clearRegister(reg);
|
||||
Scalar scalar = instr.getScalar(numOps - 1);
|
||||
if (scalar != null) {
|
||||
int newTableSizeMax = (int) scalar.getSignedValue() + 2;
|
||||
if (newTableSizeMax > 0 && newTableSizeMax < 128) {
|
||||
tableSizeMax = newTableSizeMax;
|
||||
}
|
||||
// RegisterValue rval = context.getRegisterValue(reg);
|
||||
// if (rval != null) {
|
||||
// long lval = rval.getSignedValue().longValue();
|
||||
// if (lval < 0)
|
||||
// tableIndexOffset = -lval;
|
||||
// } else {
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
hitTheGuard = true;
|
||||
}
|
||||
if ((mnemonic.compareToIgnoreCase("sub") == 0)) {
|
||||
int numOps = instr.getNumOperands();
|
||||
if (numOps > 1) {
|
||||
Register reg = instr.getRegister(numOps - 2);
|
||||
if ((reg != null)) {
|
||||
BigInteger val = context.getValue(reg, true);
|
||||
if (val == null) {
|
||||
return false;
|
||||
}
|
||||
context.clearRegister(reg);
|
||||
Scalar scalar = instr.getScalar(numOps - 1);
|
||||
if (scalar == null) {
|
||||
return false;
|
||||
}
|
||||
context.setValue(reg,
|
||||
val.add(BigInteger.valueOf(scalar.getSignedValue())));
|
||||
val = context.getValue(reg, true);
|
||||
// if (scalar != null) {
|
||||
// tableSizeMax = (int) scalar.getSignedValue() + 1;
|
||||
// RegisterValue rval = context.getRegisterValue(reg);
|
||||
// if (rval != null) {
|
||||
// long lval = rval.getSignedValue().longValue();
|
||||
// if (lval < 0)
|
||||
// tableIndexOffset = -lval;
|
||||
// } else {
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
// hitTheGuard = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address evaluateConstant(VarnodeContext context, Instruction instr, int pcodeop,
|
||||
Address constant, int size, RefType refType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluateReference(VarnodeContext context, Instruction instr, int pcodeop,
|
||||
Address address, int size, RefType refType) {
|
||||
|
||||
// if ever see a reference to 0, something went wrong, stop the process
|
||||
if (address == null) {
|
||||
return terminatePropogation(context);
|
||||
}
|
||||
|
||||
// for switches, if access is below 256, then there is a problem
|
||||
// if ever loading from instructions in memory, must EXIT!
|
||||
//
|
||||
long offset = address.getOffset();
|
||||
if ((offset >= 0 && offset < 256) || context.readExecutableCode()) {
|
||||
return terminatePropogation(context);
|
||||
}
|
||||
if (!((refType.isComputed() || refType.isConditional() == !followConditional) &&
|
||||
program.getMemory().contains(address))) {
|
||||
if (refType.isRead()) {
|
||||
if (targetList.contains(address)) {
|
||||
return terminatePropogation(context);
|
||||
}
|
||||
size = createDataType(instr, address);
|
||||
if (size != 0) {
|
||||
addrByteSize = size;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (refType.isJump() || refType.isCall()) {
|
||||
if (accessList.contains(address)) {
|
||||
return terminatePropogation(context);
|
||||
}
|
||||
long diff = Math.abs(address.subtract(targetSwitchAddr));
|
||||
// don't allow jumps backward, or too far if this is not a call
|
||||
if (refType.isCall() || diff < 32 * 1024) {
|
||||
address = flowArmThumb(program, instr, context, address,
|
||||
instr.getFlowType(), false);
|
||||
if (address != null) {
|
||||
targetList.add(address);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// no markup, computing the jump table
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean terminatePropogation(VarnodeContext context) {
|
||||
hitTheGuard = false;
|
||||
context.setReadExecutableCode();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluateDestination(VarnodeContext context, Instruction instruction) {
|
||||
return instruction.getMinAddress().equals(targetSwitchAddr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long unknownValue(VarnodeContext context, Instruction instruction,
|
||||
Varnode node) {
|
||||
if (node.isRegister()) {
|
||||
Register reg = program.getRegister(node.getAddress());
|
||||
if (reg != null) {
|
||||
// never assume for flags, or control registers
|
||||
String regName = reg.getName();
|
||||
if (regName.equals("sp")) {
|
||||
return null;
|
||||
}
|
||||
if (!regName.startsWith("r")) {
|
||||
return new Long(0);
|
||||
}
|
||||
}
|
||||
if (hitTheGuard) {
|
||||
return assumeValue;
|
||||
}
|
||||
}
|
||||
if (hitTheGuard && context.isSymbol(node)) {
|
||||
return assumeValue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean followFalseConditionalBranches() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluateSymbolicReference(VarnodeContext context, Instruction instr,
|
||||
Address address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowAccess(VarnodeContext context, Address addr) {
|
||||
accessList.add(addr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SwitchEvaluator switchEvaluator = new SwitchEvaluator();
|
||||
|
||||
// now flow with the simple block of this branch....
|
||||
|
||||
// for each unknown branch destination,
|
||||
AddressIterator iter = destSet.getAddresses(true);
|
||||
SimpleBlockModel model = new SimpleBlockModel(program);
|
||||
while (iter.hasNext() && !monitor.isCancelled()) {
|
||||
Address loc = iter.next();
|
||||
CodeBlock bl = null;
|
||||
try {
|
||||
bl = model.getFirstCodeBlockContaining(loc, monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
break;
|
||||
}
|
||||
AddressSet branchSet = new AddressSet(bl);
|
||||
CodeBlockReferenceIterator bliter;
|
||||
try {
|
||||
bliter = bl.getSources(monitor);
|
||||
while (bliter.hasNext()) {
|
||||
if (hasCallsTo(program, bl)) {
|
||||
break;
|
||||
}
|
||||
CodeBlockReference sbl = bliter.next();
|
||||
bl = sbl.getSourceBlock();
|
||||
if (bl == null) {
|
||||
continue;
|
||||
}
|
||||
if (!sbl.getFlowType().isCall()) {
|
||||
branchSet.add(bl);
|
||||
}
|
||||
if (sbl.getFlowType().isJump() && bl.getNumSources(monitor) == 1) {
|
||||
if (sbl.getFlowType().isConditional()) {
|
||||
followConditional = true;
|
||||
break;
|
||||
}
|
||||
bliter = bl.getSources(monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
break;
|
||||
}
|
||||
|
||||
switchEvaluator.init(loc, 64);
|
||||
|
||||
Instruction targetInstr = program.getListing().getInstructionAt(loc);
|
||||
|
||||
SymbolicPropogator targetEval = symEval;
|
||||
// if this is a tbX instruction, don't assume any old values
|
||||
if (targetInstr != null && targetInstr.getMnemonicString().startsWith("tb")) {
|
||||
targetEval = new SymbolicPropogator(program);
|
||||
}
|
||||
|
||||
Address zeroAddr = targetInstr.getMinAddress().getNewAddress(0);
|
||||
for (long assume = 0; assume < switchEvaluator.getTableSizeMax(); assume++) {
|
||||
switchEvaluator.initForCase(new Long(assume));
|
||||
|
||||
targetEval.flowConstants(branchSet.getMinAddress(), branchSet, switchEvaluator,
|
||||
false, monitor);
|
||||
// go around once, table might be 1 based
|
||||
if (assume > 0 && targetEval.readExecutable()) {
|
||||
break;
|
||||
}
|
||||
// if it didn't get it after try with 1
|
||||
if (assume > 1 && switchEvaluator.getTargetList().size() < 1) {
|
||||
break;
|
||||
}
|
||||
// if the target list ever contains zero, is bad
|
||||
if (switchEvaluator.getTargetList().contains(zeroAddr)) {
|
||||
switchEvaluator.getTargetList().clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// re-create the function body with the newly found code
|
||||
if (switchEvaluator.getTargetList().size() > 1) {
|
||||
Iterator<Address> liter = switchEvaluator.getTargetList().iterator();
|
||||
Address firstAddress = switchEvaluator.getTargetList().get(0);
|
||||
while (liter.hasNext()) {
|
||||
if (!firstAddress.equals(liter.next())) {
|
||||
AddressTable table;
|
||||
table = new AddressTable(loc,
|
||||
switchEvaluator.getTargetList().toArray(new Address[0]),
|
||||
switchEvaluator.getAddrByteSize(), 0, false);
|
||||
Instruction jmpInstr = program.getListing().getInstructionAt(loc);
|
||||
if (jmpInstr.getReferencesFrom().length <= 1) {
|
||||
Iterator<Address> jmpIter = switchEvaluator.getTargetList().iterator();
|
||||
while (jmpIter.hasNext()) {
|
||||
Address address = jmpIter.next();
|
||||
jmpInstr.addMnemonicReference(address, jmpInstr.getFlowType(),
|
||||
SourceType.ANALYSIS);
|
||||
}
|
||||
}
|
||||
table.disassemble(program, jmpInstr, monitor);
|
||||
table.fixupFunctionBody(program, jmpInstr, monitor);
|
||||
labelTable(program, loc, switchEvaluator.getTargetList());
|
||||
switchEvaluator.getTargetList().clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (switchEvaluator.getTargetList().size() > 0) {
|
||||
AddressTable table;
|
||||
table = new AddressTable(loc, switchEvaluator.getTargetList().toArray(new Address[0]),
|
||||
switchEvaluator.getAddrByteSize(), 0, false);
|
||||
table.disassemble(program, targetInstr,monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @return true if there are currently any call references to this CodeBlock
|
||||
*/
|
||||
private boolean hasCallsTo(Program program, CodeBlock bl) {
|
||||
Address startAddr = bl.getFirstStartAddress();
|
||||
ReferenceIterator referencesTo = program.getReferenceManager().getReferencesTo(startAddr);
|
||||
while (referencesTo.hasNext()) {
|
||||
Reference reference = referencesTo.next();
|
||||
if (reference.getReferenceType().isCall()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int createDataType(Instruction instr, Address address) {
|
||||
Program program = instr.getProgram();
|
||||
if (!program.getListing().isUndefined(address, address)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String mnemonic = instr.getMnemonicString();
|
||||
|
||||
int charOff = 0;
|
||||
if (mnemonic.startsWith("ldrex") || mnemonic.startsWith("strex")) {
|
||||
charOff = 5;
|
||||
}
|
||||
else if (mnemonic.startsWith("ldrs") || mnemonic.startsWith("strs")) {
|
||||
charOff = 4;
|
||||
}
|
||||
else if (mnemonic.startsWith("ldr") || mnemonic.startsWith("str")) {
|
||||
charOff = 3;
|
||||
}
|
||||
else if (mnemonic.startsWith("ld") || mnemonic.startsWith("st")) {
|
||||
charOff = 2;
|
||||
}
|
||||
else if (mnemonic.startsWith("tbh")) {
|
||||
charOff = 2;
|
||||
}
|
||||
else if (mnemonic.startsWith("tbb")) {
|
||||
charOff = 2;
|
||||
}
|
||||
else if (mnemonic.startsWith("vldr") || mnemonic.startsWith("vstr")) {
|
||||
charOff = mnemonic.length() - 2;
|
||||
}
|
||||
|
||||
if (charOff <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DataType dt = Undefined4DataType.dataType;
|
||||
if (mnemonic.length() > charOff) {
|
||||
char endCh = mnemonic.charAt(charOff);
|
||||
switch (endCh) {
|
||||
case '6':
|
||||
dt = Undefined8DataType.dataType;
|
||||
break;
|
||||
case '3':
|
||||
dt = Undefined4DataType.dataType;
|
||||
break;
|
||||
case 'l':
|
||||
dt = Undefined4DataType.dataType;
|
||||
break;
|
||||
case 'w':
|
||||
case 'h':
|
||||
dt = Undefined2DataType.dataType;
|
||||
break;
|
||||
case 'b':
|
||||
dt = Undefined1DataType.dataType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//new CreateDataCmd(address, dt).applyTo(program);
|
||||
Data data = null;
|
||||
try {
|
||||
data = program.getListing().createData(address, dt);
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
data = program.getListing().getDefinedDataAt(address);
|
||||
}
|
||||
catch (DataTypeConflictException e) {
|
||||
// ignore data type conflict
|
||||
}
|
||||
int addrByteSize = dt.getLength();
|
||||
//data = program.getListing().getDefinedDataAt(address);
|
||||
if (data != null) {
|
||||
Object dValue = data.getValue();
|
||||
// if the value at the location looks like a pointer, create a pointer
|
||||
if (dValue != null && dValue instanceof Scalar) {
|
||||
Scalar sValue = (Scalar) dValue;
|
||||
|
||||
long value = sValue.getUnsignedValue();
|
||||
if (value < 4096 || value == 0xffff || value == 0xff00 || value == 0xffffff ||
|
||||
value == 0xff0000 || value == 0xff00ff || value == 0xffffffff ||
|
||||
value == 0xffffff00 || value == 0xffff0000 || value == 0xff000000) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the access is a read, and the data is not far away, consider it constant
|
||||
long distance = address.getOffset() - instr.getAddress().getOffset();
|
||||
if (distance > 0 && distance < MAX_DISTANCE) {
|
||||
markDataAsConstant(data);
|
||||
}
|
||||
|
||||
// Address sAddr = address.getNewAddress(sValue.getUnsignedValue());
|
||||
// if (program.getMemory().contains(sAddr)) {
|
||||
// program.getListing().clearCodeUnits(address, address);
|
||||
// new CreateDataCmd(address, DataTypeFactory.POINTER).applyTo(program);
|
||||
// }
|
||||
}
|
||||
}
|
||||
return addrByteSize;
|
||||
}
|
||||
|
||||
private void labelTable(Program program, Address loc, ArrayList<Address> targets) {
|
||||
Namespace space = null;
|
||||
|
||||
Instruction start_inst = program.getListing().getInstructionAt(loc);
|
||||
|
||||
// not putting switch into functions anymore
|
||||
// program.getSymbolTable().getNamespace(start_inst.getMinAddress());
|
||||
String spaceName = "switch_" + start_inst.getMinAddress();
|
||||
try {
|
||||
space = program.getSymbolTable().createNameSpace(space, spaceName, SourceType.ANALYSIS);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
space = program.getSymbolTable().getNamespace(spaceName, program.getGlobalNamespace());
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
// just go with default space
|
||||
}
|
||||
|
||||
int tableNumber = 0;
|
||||
for (Address addr : targets) {
|
||||
AddLabelCmd lcmd = new AddLabelCmd(addr, "case_" + Long.toHexString(tableNumber), space,
|
||||
SourceType.ANALYSIS);
|
||||
tableNumber++;
|
||||
lcmd.setNamespace(space);
|
||||
|
||||
lcmd.applyTo(program);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disassemble at the specified target address and optionally create a mnemonic flow reference.
|
||||
* @param monitor
|
||||
* @param instruction flow from instruction
|
||||
* @param target disassembly address
|
||||
* @param flowType if not null a reference from the instruction mnemonic will be created to the specified
|
||||
* target address using this flowType.
|
||||
* @param addReference
|
||||
*/
|
||||
Address flowArmThumb(Program program, Instruction instruction, VarnodeContext context,
|
||||
Address target, FlowType flowType, boolean addReference) {
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
long bxOffset = target.getOffset();
|
||||
long thumbMode = bxOffset & 0x1;
|
||||
|
||||
Address addr = instruction.getMinAddress().getNewAddress(bxOffset & 0xfffffffe);
|
||||
|
||||
Listing listing = program.getListing();
|
||||
|
||||
if (flowType != null) {
|
||||
int opIndex = -1;
|
||||
for (int i = 0; i < instruction.getNumOperands(); i++) {
|
||||
int opType = instruction.getOperandType(i);
|
||||
// markup the program counter for any flow
|
||||
if ((opType & OperandType.REGISTER) != 0 || (opType & OperandType.DYNAMIC) != 0) {
|
||||
opIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (addReference) {
|
||||
Reference[] refsFrom = instruction.getReferencesFrom();
|
||||
boolean foundRef = false;
|
||||
for (Reference element : refsFrom) {
|
||||
if (element.getToAddress().equals(addr)) {
|
||||
// reference already there, assume thumb bit propagated
|
||||
foundRef = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundRef) {
|
||||
if (opIndex == -1) {
|
||||
instruction.addMnemonicReference(addr, flowType, SourceType.ANALYSIS);
|
||||
}
|
||||
else {
|
||||
instruction.addOperandReference(opIndex, addr, flowType,
|
||||
SourceType.ANALYSIS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tmodeRegister != null && listing.getUndefinedDataAt(addr) != null) {
|
||||
boolean inThumbMode = false;
|
||||
RegisterValue curvalue =
|
||||
context.getRegisterValue(tmodeRegister, instruction.getMinAddress());
|
||||
if (curvalue != null && curvalue.hasValue()) {
|
||||
inThumbMode = (curvalue.getUnsignedValue().intValue() == 1);
|
||||
}
|
||||
// if the TB register is set, that trumps any mode we are tracking
|
||||
RegisterValue tbvalue = context.getRegisterValue(tbRegister);
|
||||
if (tbvalue != null && tbvalue.hasValue()) {
|
||||
inThumbMode = (tbvalue.getUnsignedValue().intValue() == 1);
|
||||
}
|
||||
else {
|
||||
// blx instruction on a direct address in ARM mode always goes to thumb mode
|
||||
if (instruction.getMnemonicString().equals("blx") || thumbMode != 0) {
|
||||
inThumbMode = true;
|
||||
}
|
||||
}
|
||||
BigInteger thumbModeValue = BigInteger.valueOf(inThumbMode ? 1 : 0);
|
||||
try {
|
||||
program.getProgramContext().setValue(tmodeRegister, addr, addr, thumbModeValue);
|
||||
}
|
||||
catch (ContextChangeException e) {
|
||||
Msg.error(this, "Unexpected Exception", e);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
// instruction already there
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disassemble at the specified target address and optionally create a mnemonic flow reference.
|
||||
* @param monitor
|
||||
* @param instruction flow from instruction
|
||||
* @param target disassembly address
|
||||
* @param flowType if not null a reference from the instruction mnemonic will be created to the specified
|
||||
* target address using this flowType.
|
||||
* @param addRef true if a reference should be added.
|
||||
*
|
||||
*/
|
||||
void doArmThumbDisassembly(Program program, Instruction instruction, VarnodeContext context,
|
||||
Address target, FlowType flowType, boolean addRef, TaskMonitor monitor) {
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
target = flowArmThumb(program, instruction, context, target, flowType, addRef);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// this is here so the reference gets created, but not - disassembled if it is in a bad part of memory.
|
||||
// something computed it into the memory
|
||||
MemoryBlock block = program.getMemory().getBlock(target);
|
||||
if (block == null || !block.isExecute() || !block.isInitialized() ||
|
||||
block.getName().equals("EXTERNAL")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Disassembler dis = Disassembler.getDisassembler(program, monitor, null);
|
||||
AddressSet disassembleAddrs = dis.disassemble(target, null);
|
||||
AutoAnalysisManager.getAnalysisManager(program).codeDefined(disassembleAddrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(Options options, Program program) {
|
||||
super.optionsChanged(options, program);
|
||||
|
||||
options.registerOption(SWITCH_OPTION_NAME, recoverSwitchTables, null,
|
||||
SWITCH_OPTION_DESCRIPTION);
|
||||
recoverSwitchTables = options.getBoolean(SWITCH_OPTION_NAME, recoverSwitchTables);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.elf.extend;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import ghidra.app.util.bin.format.elf.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.listing.ContextChangeException;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class ARM_ElfExtension extends ElfExtension {
|
||||
|
||||
// Elf Program Header Extensions
|
||||
public static final ElfProgramHeaderType PT_ARM_EXIDX =
|
||||
new ElfProgramHeaderType(0x70000000, "PT_ARM_EXIDX", "Frame unwind information");
|
||||
|
||||
// Elf Section Header Extensions
|
||||
public static final ElfSectionHeaderType SHT_ARM_EXIDX =
|
||||
new ElfSectionHeaderType(0x70000001, "SHT_ARM_EXIDX", "Exception Index table");
|
||||
public static final ElfSectionHeaderType SHT_ARM_PREEMPTMAP = new ElfSectionHeaderType(
|
||||
0x70000002, "SHT_ARM_PREEMPTMAP", "BPABI DLL dynamic linking preemption map");
|
||||
public static final ElfSectionHeaderType SHT_ARM_ATTRIBUTES = new ElfSectionHeaderType(
|
||||
0x70000003, "SHT_ARM_ATTRIBUTES", "Object file compatibility attributes");
|
||||
public static final ElfSectionHeaderType SHT_ARM_DEBUGOVERLAY =
|
||||
new ElfSectionHeaderType(0x70000004, "SHT_ARM_DEBUGOVERLAY", "See DBGOVL for details");
|
||||
public static final ElfSectionHeaderType SHT_ARM_OVERLAYSECTION =
|
||||
new ElfSectionHeaderType(0x70000005, "SHT_ARM_OVERLAYSECTION",
|
||||
"See Debugging Overlaid Programs (DBGOVL) for details");
|
||||
|
||||
@Override
|
||||
public boolean canHandle(ElfHeader elf) {
|
||||
return elf.e_machine() == ElfConstants.EM_ARM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(ElfLoadHelper elfLoadHelper) {
|
||||
Language language = elfLoadHelper.getProgram().getLanguage();
|
||||
return canHandle(elfLoadHelper.getElfHeader()) &&
|
||||
"ARM".equals(language.getProcessor().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDataTypeSuffix() {
|
||||
return "_ARM";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processElf(ElfLoadHelper elfLoadHelper, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
Register tmodeRegister = elfLoadHelper.getProgram().getRegister("TMode");
|
||||
if (tmodeRegister == null) {
|
||||
elfLoadHelper.log("WARNING: TMode register not found - Thumb mode not supported");
|
||||
}
|
||||
// TODO: markup PT_ARM_EXIDX ?s
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address creatingFunction(ElfLoadHelper elfLoadHelper, Address functionAddress) {
|
||||
Program program = elfLoadHelper.getProgram();
|
||||
if ((functionAddress.getOffset() & 1) != 0) {
|
||||
Register tmodeRegister = program.getRegister("TMode");
|
||||
if (tmodeRegister == null) {
|
||||
elfLoadHelper.log("TMode mode not supported, unable to mark address as Thumb: " +
|
||||
functionAddress);
|
||||
return functionAddress;
|
||||
}
|
||||
functionAddress = functionAddress.previous(); // align address
|
||||
try {
|
||||
program.getProgramContext().setValue(tmodeRegister, functionAddress,
|
||||
functionAddress, BigInteger.ONE);
|
||||
}
|
||||
catch (ContextChangeException e) {
|
||||
// ignore since should not be instructions at time of import
|
||||
}
|
||||
}
|
||||
if ((functionAddress.getOffset() % 4) == 2) {//The combination bit[1:0] = 0b10 is reserved.
|
||||
elfLoadHelper.log("Function address is two bit aligned (reserved per ARM manual): " +
|
||||
functionAddress);
|
||||
}
|
||||
return functionAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address evaluateElfSymbol(ElfLoadHelper elfLoadHelper, ElfSymbol elfSymbol,
|
||||
Address address, boolean isExternal) {
|
||||
|
||||
if (isExternal) {
|
||||
return address;
|
||||
}
|
||||
|
||||
Program program = elfLoadHelper.getProgram();
|
||||
|
||||
String symName = elfSymbol.getNameAsString();
|
||||
|
||||
try {
|
||||
Register tmodeRegister = program.getRegister("TMode");
|
||||
|
||||
// ELF ARM - tags ARM code with $a and Thumb code with $t
|
||||
//
|
||||
if (tmodeRegister == null) {
|
||||
// Thumb Mode not supported by language
|
||||
}
|
||||
else if ("$t".equals(symName) || symName.startsWith("$t.")) {
|
||||
// is thumb mode
|
||||
program.getProgramContext().setValue(tmodeRegister, address, address,
|
||||
BigInteger.valueOf(1));
|
||||
elfLoadHelper.markAsCode(address);
|
||||
|
||||
// do not retain $t symbols in program due to potential function/thunk naming interference
|
||||
elfLoadHelper.setElfSymbolAddress(elfSymbol, address);
|
||||
return null;
|
||||
}
|
||||
else if ("$a".equals(symName) || symName.startsWith("$a.")) {
|
||||
// is arm mode
|
||||
program.getProgramContext().setValue(tmodeRegister, address, address,
|
||||
BigInteger.valueOf(0));
|
||||
elfLoadHelper.markAsCode(address);
|
||||
|
||||
// do not retain $a symbols in program due to potential function/thunk naming interference
|
||||
elfLoadHelper.setElfSymbolAddress(elfSymbol, address);
|
||||
return null;
|
||||
}
|
||||
else if ("$b".equals(symName)) {
|
||||
// don't do anything this is data
|
||||
}
|
||||
else if ("$d".equals(symName) || symName.startsWith("$d.")) {
|
||||
// is data, need to protect as data
|
||||
elfLoadHelper.createUndefinedData(address, (int) elfSymbol.getSize());
|
||||
|
||||
// do not retain $d symbols in program due to excessive duplicate symbols
|
||||
elfLoadHelper.setElfSymbolAddress(elfSymbol, address);
|
||||
return null;
|
||||
}
|
||||
if (elfSymbol.getType() == ElfSymbol.STT_FUNC) {
|
||||
long symVal = address.getOffset();
|
||||
if ((symVal & 1) != 0 && tmodeRegister != null) {
|
||||
address = address.previous();
|
||||
program.getProgramContext().setValue(tmodeRegister, address, address,
|
||||
BigInteger.valueOf(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ContextChangeException e) {
|
||||
// ignore since should not be instructions at time of import
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void processGotPlt(ElfLoadHelper elfLoadHelper, TaskMonitor monitor) throws CancelledException {
|
||||
// TODO override GOT markup. PLT handled by R_ARM_JUMP_SLOT relocation processing.
|
||||
// }
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.elf.extend;
|
||||
|
||||
public class ARM_ElfProgramHeaderConstants {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** Masks bits describing the format of data in subsequent words. The masked value is described in Table 5-3, below. */
|
||||
public static final int PT_ARM_ARCHEXT_FMTMSK = 0xff000000;
|
||||
/** Masks bits describing the architecture profile required by the executable. The masked value is described in Table 5-4, below. */
|
||||
public static final int PT_ARM_ARCHEXT_PROFMSK = 0x00ff0000;
|
||||
/** Masks bits describing the base architecture required by the executable. The masked value is described in Table 5-5, below. */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHMSK = 0x000000ff;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Table 5-3, Architecture compatibility data formats lists the architecture
|
||||
// compatibility data formats defined by this ABI. All other format
|
||||
// identifiers are reserved to future revisions of this specification.
|
||||
|
||||
/** There are no additional words of data. However, if EF_OSABI is non-zero, the relevant platform ABI may define additional data that follows the initial word. */
|
||||
public static final int PT_ARM_ARCHEXT_FMT_OS = 0x00000000;
|
||||
/** 5.2.1.1, below describes the format of the following data words. */
|
||||
public static final int PT_ARM_ARCHEXT_FMT_ABI = 0x01000000;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Table 5-4, Architecture profile compatibility data.
|
||||
// Lists the values specifying the architectural profile needed by an executable file.
|
||||
|
||||
/** The architecture has no profile variants, or the image has no profile-specific constraints */
|
||||
public static final int PT_ARM_ARCHEXT_PROF_NONE = 0x0;
|
||||
/** The executable file requires the Application profile */
|
||||
public static final int PT_ARM_ARCHEXT_PROF_ARM = 'A' << 16;
|
||||
/** The executable file requires the Real-Time profile */
|
||||
public static final int PT_ARM_ARCHEXT_PROF_RT = 'R' << 16;
|
||||
/** The executable file requires the Microcontroller profile */
|
||||
public static final int PT_ARM_ARCHEXT_PROF_MC = 'M' << 16;
|
||||
/** The executable file requires the 'classic' ('A' or 'R' profile) exception model. */
|
||||
public static final int PT_ARM_ARCHEXT_PROF_CLASSIC = 'S' << 16;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//Table 5-5, Architecture version compatibility data defines the values that
|
||||
//specify the minimum architecture version needed by this executable file.
|
||||
//These values are identical to those of the Tag_CPU_arch attribute used
|
||||
//in the attributes section of a relocatable file.
|
||||
|
||||
/** The needed architecture is unknown or specified in some other way */
|
||||
public static final int PT_ARM_ARCHEXT_ARCH_UNKN = 0x00;
|
||||
/** Architecture v4 */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHv4 = 0x01;
|
||||
/** Architecture v4T */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHv4T = 0x02;
|
||||
/** Architecture v5T */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHv5T = 0x03;
|
||||
/** Architecture v5TE */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHv5TE = 0x04;
|
||||
/** Architecture v5TEJ */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHv5TEJ = 0x05;
|
||||
/** Architecture v6 */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHv6 = 0x06;
|
||||
/** Architecture v6KZ */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHv6KZ = 0x07;
|
||||
/** Architecture v6T2 */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHv6T2 = 0x08;
|
||||
/** Architecture v6K */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHv6K = 0x09;
|
||||
/** Architecture v7 (in this case the architecture profile may also be required to fully specify the needed execution environment) */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHv7 = 0x0A;
|
||||
/** Architecture v6M (e.g. Cortex M0) */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHv6M = 0x0B;
|
||||
/** Architecture v6S-M (e.g. Cortex M0) */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHv6SM = 0x0C;
|
||||
/** Architecture v7E-M */
|
||||
public static final int PT_ARM_ARCHEXT_ARCHv7EM = 0x0D;
|
||||
|
||||
// FLAGS
|
||||
|
||||
/** This masks an 8-bit version number, the version of the ABI to which this ELF file conforms. This ABI is version 5. A value of 0 denotes unknown conformance. */
|
||||
public static final int EF_ARM_EABIMASK = 0xFF000000;
|
||||
/** The ELF file contains BE-8 code, suitable for execution on an ARM Architecture v6 processor. This flag must only be set on an executable file. */
|
||||
public static final int EF_ARM_BE8 = 0x00800000;
|
||||
|
||||
}
|
|
@ -0,0 +1,285 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.elf.relocation;
|
||||
|
||||
public class ARM_ElfRelocationConstants {
|
||||
|
||||
/** No operation needed */
|
||||
public static final int R_ARM_NONE = 0;
|
||||
/** ((S + A) | T) - P [DEPRECATED] */
|
||||
public static final int R_ARM_PC24 = 1;
|
||||
/** (S + A) | T */
|
||||
public static final int R_ARM_ABS32 = 2;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_REL32 = 3;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_LDR_PC_G0 = 4;
|
||||
/** S + A */
|
||||
public static final int R_ARM_ABS16 = 5;
|
||||
/** S + A */
|
||||
public static final int R_ARM_ABS12 = 6;
|
||||
/** S + A */
|
||||
public static final int R_ARM_THM_ABS5 = 7;
|
||||
/** S + A */
|
||||
public static final int R_ARM_ABS_8 = 8;
|
||||
/** ((S + A) | T) - B(S) */
|
||||
public static final int R_ARM_SBREL32 = 9;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_THM_CALL = 10;
|
||||
/** S + A - Pa */
|
||||
public static final int R_ARM_THM_PC8 = 11;
|
||||
/** DELTA(B(S)) + A */
|
||||
public static final int R_ARM_BREL_ADJ = 12;
|
||||
public static final int R_ARM_TLS_DESC = 13;
|
||||
/** [OBSOLETE] */
|
||||
public static final int R_ARM_THM_SWI8 = 14;
|
||||
/** [OBSOLETE] */
|
||||
public static final int R_ARM_XPC25 = 15;
|
||||
/** [OBSOLETE] */
|
||||
public static final int R_ARM_THM_XPC22 = 16;
|
||||
/** Module[S] */
|
||||
public static final int R_ARM_TLS_DTPMOD32 = 17;
|
||||
/** S + A - TLS */
|
||||
public static final int R_ARM_TLS_DTPOFF32 = 18;
|
||||
/** S + A - TLS */
|
||||
public static final int R_ARM_TLS_TPOFF32 = 19;
|
||||
/** Miscellaneous */
|
||||
public static final int R_ARM_COPY = 20;
|
||||
/** (S + A) | T */
|
||||
public static final int R_ARM_GLOB_DAT = 21;
|
||||
/** (S + A) | T */
|
||||
public static final int R_ARM_JUMP_SLOT = 22;
|
||||
/** B(S) + A [Note: see Table 4-16] */
|
||||
public static final int R_ARM_RELATIVE = 23;
|
||||
/** ((S + A) | T) - GOT_ORG */
|
||||
public static final int R_ARM_GOTOFF32 = 24;
|
||||
/** B(S) + A - P */
|
||||
public static final int R_ARM_BASE_PREL = 25;
|
||||
/** GOT(S) + A - GOT_ORG */
|
||||
public static final int R_ARM_GOT_BREL = 26;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_GOT_PLT32 = 27;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_CALL = 28;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_JUMP24 = 29;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_THM_JUMP24 = 30;
|
||||
/** B(S) + A */
|
||||
public static final int R_ARM_BASE_ABS = 31;
|
||||
/** Obsolete */
|
||||
public static final int R_ARM_ALU_PCREL_7_0 = 32;
|
||||
/** Obsolete */
|
||||
public static final int R_ARM_ALU_PCREL_15_8 = 33;
|
||||
/** Obsolete */
|
||||
public static final int R_ARM_ALU_PCREL_23_15 = 34;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_LDR_SBREL_11_0_NC = 35;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_ALU_SBREL_19_12_NC = 36;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_ALU_SBREL_27_20_CK = 37;
|
||||
/** (S + A) | T or ((S + A) | T) - P */
|
||||
public static final int R_ARM_TARGET1 = 38;
|
||||
/** ((S + A) | T) - B(S) */
|
||||
public static final int R_ARM_SBREL31 = 39;
|
||||
/** Miscellaneous */
|
||||
public static final int R_ARM_V4BX = 40;
|
||||
/** Miscellaneous */
|
||||
public static final int R_ARM_TARGET2 = 41;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_PREL31 = 42;
|
||||
/** (S + A) | T */
|
||||
public static final int R_ARM_MOVW_ABS_NC = 43;
|
||||
/** S + A */
|
||||
public static final int R_ARM_MOVT_ABS = 44;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_MOVW_PREL_NC = 45;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_MOVT_PREL = 46;
|
||||
/** (S + A) | T */
|
||||
public static final int R_ARM_THM_MOVW_ABS_NC = 47;
|
||||
/** S + A */
|
||||
public static final int R_ARM_THM_MOVT_ABS = 48;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_THM_MOVW_PREL_NC = 49;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_THM_MOVT_PREL = 50;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_THM_JUMP19 = 51;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_THM_JUMP6 = 52;
|
||||
/** ((S + A) | T) - Pa */
|
||||
public static final int R_ARM_THM_ALU_PREL_11_0 = 53;
|
||||
/** S + A - Pa */
|
||||
public static final int R_ARM_THM_PC12 = 54;
|
||||
/** S + A */
|
||||
public static final int R_ARM_ABS32_NOI = 55;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_REL32_NOI = 56;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_ALU_PC_G0_NC = 57;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_ALU_PC_G0 = 58;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_ALU_PC_G1_NC = 59;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_ALU_PC_G1 = 60;
|
||||
/** ((S + A) | T) - P */
|
||||
public static final int R_ARM_ALU_PC_G2 = 61;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_LDR_PC_G1 = 62;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_LDR_PC_G2 = 63;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_LDRS_PC_G0 = 64;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_LDRS_PC_G1 = 65;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_LDRS_PC_G2 = 66;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_LDC_PC_G0 = 67;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_LDC_PC_G1 = 68;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_LDC_PC_G2 = 69;
|
||||
/** ((S + A) | T) - B(S) */
|
||||
public static final int R_ARM_ALU_SB_G0_NC = 70;
|
||||
/** ((S + A) | T) - B(S) */
|
||||
public static final int R_ARM_ALU_SB_G0 = 71;
|
||||
/** ((S + A) | T) - B(S) */
|
||||
public static final int R_ARM_ALU_SB_G1_NC = 72;
|
||||
/** ((S + A) | T) - B(S) */
|
||||
public static final int R_ARM_ALU_SB_G1 = 73;
|
||||
/** ((S + A) | T) - B(S) */
|
||||
public static final int R_ARM_ALU_SB_G2 = 74;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_LDR_SB_G0 = 75;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_LDR_SB_G1 = 76;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_LDR_SB_G2 = 77;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_LDRS_SB_G0 = 78;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_LDRS_SB_G1 = 79;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_LDRS_SB_G2 = 80;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_LDC_SB_G0 = 81;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_LDC_SB_G1 = 82;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_LDC_SB_G2 = 83;
|
||||
/** ((S + A) | T) - B(S) */
|
||||
public static final int R_ARM_MOVW_BREL_NC = 84;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_MOVT_BREL = 85;
|
||||
/** ((S + A) | T) - B(S) */
|
||||
public static final int R_ARM_MOVW_BREL = 86;
|
||||
/** ((S + A) | T) - B(S) */
|
||||
public static final int R_ARM_THM_MOVW_BREL_NC = 87;
|
||||
/** S + A - B(S) */
|
||||
public static final int R_ARM_THM_MOVT_BREL = 88;
|
||||
/** ((S + A) | T) - B(S) */
|
||||
public static final int R_ARM_THM_MOVW_BREL = 89;
|
||||
/** ? */
|
||||
public static final int R_ARM_TLS_GOTDESC = 90;
|
||||
/** ? */
|
||||
public static final int R_ARM_TLS_CALL = 91;
|
||||
/** TLS relaxation */
|
||||
public static final int R_ARM_TLS_DESCSEQ = 92;
|
||||
/** ? */
|
||||
public static final int R_ARM_THM_TLS_CALL = 93;
|
||||
/** PLT(S) + A */
|
||||
public static final int R_ARM_PLT32_ABS = 94;
|
||||
/** GOT(S) + A */
|
||||
public static final int R_ARM_GOT_ABS = 95;
|
||||
/** GOT(S) + A - P */
|
||||
public static final int R_ARM_GOT_PREL = 96;
|
||||
/** GOT(S) + A - GOT_ORG */
|
||||
public static final int R_ARM_GOT_BREL12 = 97;
|
||||
/** S + A - GOT_ORG */
|
||||
public static final int R_ARM_GOTOFF12 = 98;
|
||||
/** ? */
|
||||
public static final int R_ARM_GOTRELAX = 99;
|
||||
/** ? */
|
||||
public static final int R_ARM_GNU_VTENTRY = 100;
|
||||
/** ? */
|
||||
public static final int R_ARM_GNU_VTINHERIT = 101;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_THM_JUMP11 = 102;
|
||||
/** S + A - P */
|
||||
public static final int R_ARM_THM_JUMP8 = 103;
|
||||
/** GOT(S) + A - P */
|
||||
public static final int R_ARM_TLS_GD32 = 104;
|
||||
/** GOT(S) + A - P */
|
||||
public static final int R_ARM_TLS_LDM32 = 105;
|
||||
/** S + A - TLS */
|
||||
public static final int R_ARM_TLS_LDO32 = 106;
|
||||
/** GOT(S) + A - P */
|
||||
public static final int R_ARM_TLS_IE32 = 107;
|
||||
/** S + A - tp */
|
||||
public static final int R_ARM_TLS_LE32 = 108;
|
||||
/** S + A - TLS */
|
||||
public static final int R_ARM_TLS_LDO12 = 109;
|
||||
/** S + A - tp */
|
||||
public static final int R_ARM_TLS_LE12 = 110;
|
||||
/** GOT(S) + A - GOT_ORG */
|
||||
public static final int R_ARM_TLS_IE12GP = 111;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_0 = 112;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_1 = 113;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_2 = 114;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_3 = 115;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_4 = 116;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_5 = 117;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_6 = 118;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_7 = 119;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_8 = 120;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_9 = 121;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_10 = 122;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_11 = 123;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_12 = 124;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_13 = 125;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_14 = 126;
|
||||
/** ? */
|
||||
public static final int R_ARM_PRIVATE_15 = 127;
|
||||
/** ? */
|
||||
public static final int R_ARM_ME_TOO = 128;
|
||||
/** ? */
|
||||
public static final int R_ARM_THM_TLS_DESCSEQ16 = 129;
|
||||
/** ? */
|
||||
public static final int R_ARM_THM_TLS_DESCSEQ32 = 130;
|
||||
|
||||
private ARM_ElfRelocationConstants() {
|
||||
// no construct
|
||||
}
|
||||
}
|
|
@ -0,0 +1,611 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.elf.relocation;
|
||||
|
||||
import ghidra.app.util.bin.format.elf.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.util.exception.NotFoundException;
|
||||
|
||||
public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
|
||||
@Override
|
||||
public boolean canRelocate(ElfHeader elf) {
|
||||
return elf.e_machine() == ElfConstants.EM_ARM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void relocate(ElfRelocationContext elfRelocationContext, ElfRelocation relocation,
|
||||
Address relocationAddress) throws MemoryAccessException, NotFoundException {
|
||||
|
||||
ElfHeader elf = elfRelocationContext.getElfHeader();
|
||||
if (elf.e_machine() != ElfConstants.EM_ARM) {
|
||||
return;
|
||||
}
|
||||
|
||||
Program program = elfRelocationContext.getProgram();
|
||||
|
||||
Memory memory = program.getMemory();
|
||||
|
||||
int type = relocation.getType();
|
||||
if (type == ARM_ElfRelocationConstants.R_ARM_NONE) {
|
||||
return;
|
||||
}
|
||||
int symbolIndex = relocation.getSymbolIndex();
|
||||
|
||||
long addend = relocation.getAddend(); // will be 0 for REL case
|
||||
|
||||
ElfSymbol sym = elfRelocationContext.getSymbol(symbolIndex);
|
||||
String symbolName = sym.getNameAsString();
|
||||
|
||||
boolean isThumb = isThumb(sym);
|
||||
|
||||
long offset = (int) relocationAddress.getOffset();
|
||||
|
||||
long symbolValue = elfRelocationContext.getSymbolValue(sym);
|
||||
|
||||
int newValue = 0;
|
||||
|
||||
switch (type) {
|
||||
case ARM_ElfRelocationConstants.R_ARM_PC24: {
|
||||
int oldValue = memory.getInt(relocationAddress);
|
||||
newValue = (int) (symbolValue + addend);
|
||||
newValue -= (offset + 8); // PC relative, PC will be 8 bytes after inst start
|
||||
if (isThumb) {
|
||||
newValue |= 1;
|
||||
}
|
||||
// is this a BLX instruction, must put the lower half word in bit24
|
||||
if ((oldValue & 0xf0000000) == 0xf0000000) {
|
||||
newValue = (oldValue & 0xfe000000) | (((newValue >> 1) & 1) << 24) |
|
||||
((newValue >> 2) & 0x00ffffff);
|
||||
}
|
||||
else {
|
||||
newValue = (oldValue & 0xff000000) | ((newValue >> 2) & 0x00ffffff);
|
||||
}
|
||||
memory.setInt(relocationAddress, newValue);
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ABS32: {
|
||||
int oldValue = memory.getInt(relocationAddress);
|
||||
newValue = (int) (symbolValue + addend + oldValue);
|
||||
if (isThumb) {
|
||||
newValue |= 1;
|
||||
}
|
||||
memory.setInt(relocationAddress, newValue);
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_REL32: {
|
||||
newValue = (int) (symbolValue + addend);
|
||||
newValue -= (offset + 8); // PC relative, PC will be 8 bytes after inst start
|
||||
if (isThumb) {
|
||||
newValue |= 1;
|
||||
}
|
||||
memory.setInt(relocationAddress, newValue);
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDR_PC_G0: {
|
||||
int oldValue = memory.getInt(relocationAddress);
|
||||
newValue = (int) (symbolValue + addend);
|
||||
newValue -= (offset + 8); // PC relative, PC will be 8 bytes after inst start
|
||||
newValue = (oldValue & 0xff7ff000) | ((~(newValue >> 31) & 1) << 23) |
|
||||
((newValue >> 2) & 0xfff);
|
||||
memory.setInt(relocationAddress, newValue);
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ABS16: {
|
||||
short sValue = (short) (symbolValue + addend);
|
||||
memory.setShort(relocationAddress, sValue);
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ABS12: {
|
||||
int oldValue = memory.getInt(relocationAddress);
|
||||
newValue = (int) (symbolValue + addend);
|
||||
newValue = (oldValue & 0xfffff000) | (newValue & 0x00000fff);
|
||||
memory.setInt(relocationAddress, newValue);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_ABS5: {
|
||||
break;
|
||||
}
|
||||
*/
|
||||
case ARM_ElfRelocationConstants.R_ARM_ABS_8: {
|
||||
byte bValue = (byte) (symbolValue + addend);
|
||||
memory.setByte(relocationAddress, bValue);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
case ARM_ElfRelocationConstants.R_ARM_SBREL32: {
|
||||
break;
|
||||
}
|
||||
*/
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_JUMP24:
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_CALL: {
|
||||
|
||||
newValue = (int) (symbolValue + addend);
|
||||
// since it is adding in the oldvalue below, don't need to add in 4 for pc offset
|
||||
newValue -= (offset);
|
||||
|
||||
short oldValueH = memory.getShort(relocationAddress);
|
||||
short oldValueL = memory.getShort(relocationAddress.add(2));
|
||||
boolean isBLX = (oldValueL & 0x1000) == 0;
|
||||
|
||||
int s = (oldValueH & (1 << 10)) >> 10;
|
||||
int upper = oldValueH & 0x3ff;
|
||||
int lower = oldValueL & 0x7ff;
|
||||
int j1 = (oldValueL & (1 << 13)) >> 13;
|
||||
int j2 = (oldValueL & (1 << 11)) >> 11;
|
||||
int i1 = (j1 != s) ? 0 : 1;
|
||||
int i2 = (j2 != s) ? 0 : 1;
|
||||
int origaddend = (i1 << 23) | (i2 << 22) | (upper << 12) | (lower << 1);
|
||||
origaddend = (origaddend | ((s ^ 1) << 24)) - (1 << 24);
|
||||
|
||||
newValue = newValue + origaddend;
|
||||
|
||||
newValue = newValue >> 1;
|
||||
// for Thumb, have to be careful, LE is swapped on 2 bytes
|
||||
short newValueH = (short) ((oldValueH & 0xf800) | (((newValue >> 11) & 0x00007ff)));
|
||||
short newValueL = (short) ((oldValueL & 0xf800) | (newValue & 0x00007ff));
|
||||
|
||||
if (isBLX) {
|
||||
newValueL &= 0xfffe;
|
||||
}
|
||||
|
||||
memory.setShort(relocationAddress, newValueH);
|
||||
memory.setShort(relocationAddress.add(2), newValueL);
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_PC8: {
|
||||
short oldValue = memory.getShort(relocationAddress);
|
||||
newValue = (int) (symbolValue + addend);
|
||||
newValue -= (offset + 4); // PC relative, PC will be 4 bytes past inst start
|
||||
newValue = newValue >> 1;
|
||||
short sValue = (short) ((oldValue & 0xff00) | (newValue & 0x00ff));
|
||||
memory.setShort(relocationAddress, sValue);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
case ARM_ElfRelocationConstants.R_ARM_BREL_ADJ: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_DESC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_SWI8: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_XPC25: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_XPC22: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_DTPMOD32: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_DTPOFF32: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_TPOFF32: {
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
case ARM_ElfRelocationConstants.R_ARM_GLOB_DAT: {
|
||||
// Corresponds to resolved local/EXTERNAL symbols within GOT
|
||||
if (elfRelocationContext.extractAddend()) {
|
||||
addend = memory.getInt(relocationAddress);
|
||||
}
|
||||
newValue = (int) (symbolValue + addend);
|
||||
if (isThumb) {
|
||||
newValue |= 1;
|
||||
}
|
||||
memory.setInt(relocationAddress, newValue);
|
||||
break;
|
||||
}
|
||||
|
||||
case ARM_ElfRelocationConstants.R_ARM_JUMP_SLOT: {
|
||||
// Corresponds to lazy dynamically linked external symbols within
|
||||
// GOT/PLT symbolValue corresponds to PLT entry for which we need to
|
||||
// create and external function location. Don't bother changing
|
||||
// GOT entry bytes if it refers to .plt block
|
||||
Address symAddress = elfRelocationContext.getSymbolAddress(sym);
|
||||
MemoryBlock block = memory.getBlock(symAddress);
|
||||
boolean isPltSym = block != null && block.getName().startsWith(".plt");
|
||||
boolean isExternalSym =
|
||||
block != null && MemoryBlock.EXTERNAL_BLOCK_NAME.equals(block.getName());
|
||||
if (!isPltSym) {
|
||||
memory.setInt(relocationAddress, (int) symAddress.getOffset());
|
||||
}
|
||||
if (isPltSym || isExternalSym) {
|
||||
Function extFunction =
|
||||
elfRelocationContext.getLoadHelper().createExternalFunctionLinkage(
|
||||
symbolName, symAddress, null);
|
||||
if (extFunction == null) {
|
||||
markAsError(program, relocationAddress, "R_ARM_JUMP_SLOT", symbolName,
|
||||
"Failed to create R_ARM_JUMP_SLOT external function",
|
||||
elfRelocationContext.getLog());
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ARM_ElfRelocationConstants.R_ARM_RELATIVE: {
|
||||
if (elfRelocationContext.extractAddend()) {
|
||||
addend = memory.getInt(relocationAddress);
|
||||
}
|
||||
newValue =
|
||||
(int) elfRelocationContext.getImageBaseWordAdjustmentOffset() + (int) addend;
|
||||
memory.setInt(relocationAddress, newValue);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
case ARM_ElfRelocationConstants.R_ARM_GOTOFF32: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_BASE_PREL: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_GOT_BREL: {
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
case ARM_ElfRelocationConstants.R_ARM_JUMP24:
|
||||
case ARM_ElfRelocationConstants.R_ARM_CALL:
|
||||
case ARM_ElfRelocationConstants.R_ARM_GOT_PLT32:
|
||||
int oldValue = memory.getInt(relocationAddress);
|
||||
newValue = (int) (symbolValue + addend);
|
||||
|
||||
newValue -= (offset + 8); // PC relative, PC will be 8 bytes past inst start
|
||||
|
||||
// is this a BLX instruction, must put the lower half word in bit24
|
||||
// TODO: this might not appear on a BLX, but just in case
|
||||
if ((oldValue & 0xff000000) == 0xfb000000) {
|
||||
newValue = (oldValue & 0xfe000000) | (((newValue >> 1) & 1) << 24) |
|
||||
((newValue >> 2) & 0x00ffffff);
|
||||
}
|
||||
else {
|
||||
newValue = (oldValue & 0xff000000) | ((newValue >> 2) & 0x00ffffff);
|
||||
}
|
||||
memory.setInt(relocationAddress, newValue);
|
||||
break;
|
||||
|
||||
/*
|
||||
case ARM_ElfRelocationConstants.R_ARM_BASE_ABS: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_PCREL_7_0: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_PCREL_15_8: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_PCREL_23_15: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDR_SBREL_11_0_NC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_SBREL_19_12_NC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_SBREL_27_20_CK: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TARGET1: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_SBREL31: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_V4BX: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TARGET2: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PREL31: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_MOVW_ABS_NC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_MOVT_ABS: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_MOVW_PREL_NC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_MOVT_PREL: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_MOVW_ABS_NC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_MOVT_ABS: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_MOVW_PREL_NC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_MOVT_PREL: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_JUMP19: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_JUMP6: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_ALU_PREL_11_0: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_PC12: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ABS32_NOI: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_REL32_NOI: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_PC_G0_NC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_PC_G0: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_PC_G1_NC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_PC_G1: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_PC_G2: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDR_PC_G1: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDR_PC_G2: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDRS_PC_G0: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDRS_PC_G1: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDRS_PC_G2: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDC_PC_G0: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDC_PC_G1: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDC_PC_G2: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_SB_G0_NC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_SB_G0: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_SB_G1_NC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_SB_G1: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_ALU_SB_G2: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDR_SB_G0: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDR_SB_G1: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDR_SB_G2: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDRS_SB_G0: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDRS_SB_G1: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDRS_SB_G2: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDC_SB_G0: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDC_SB_G1: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_LDC_SB_G2: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_MOVW_BREL_NC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_MOVT_BREL: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_MOVW_BREL: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_MOVW_BREL_NC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_MOVT_BREL: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_MOVW_BREL: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_GOTDESC: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_CALL: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_DESCSEQ: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_TLS_CALL: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PLT32_ABS: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_GOT_ABS: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_GOT_PREL: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_GOT_BREL12: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_GOTOFF12: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_GOTRELAX: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_GNU_VTENTRY: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_GNU_VTINHERIT: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_JUMP11: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_JUMP8: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_GD32: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_LDM32: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_LDO32: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_IE32: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_LE32: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_LDO12: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_LE12: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_TLS_IE12GP: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_0: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_1: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_2: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_3: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_4: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_5: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_6: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_7: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_8: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_9: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_10: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_11: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_12: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_13: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_14: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_PRIVATE_15: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_TLS_DESCSEQ16: {
|
||||
break;
|
||||
}
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_TLS_DESCSEQ32: {
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
case ARM_ElfRelocationConstants.R_ARM_COPY: {
|
||||
markAsWarning(program, relocationAddress, "R_ARM_COPY", symbolName, symbolIndex,
|
||||
"Runtime copy not supported", elfRelocationContext.getLog());
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName,
|
||||
elfRelocationContext.getLog());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isThumb(ElfSymbol symbol) {
|
||||
if (symbol.isFunction() && (symbol.getValue() % 1) == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.elf.relocation;
|
||||
|
||||
import ghidra.app.plugin.core.reloc.RelocationFixupHandler;
|
||||
import ghidra.app.util.opinion.ElfLoader;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Processor;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.reloc.Relocation;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
|
||||
public class ElfArmRelocationFixupHandler extends RelocationFixupHandler {
|
||||
|
||||
@Override
|
||||
public boolean processRelocation(Program program, Relocation relocation, Address oldImageBase,
|
||||
Address newImageBase) throws MemoryAccessException, CodeUnitInsertionException {
|
||||
|
||||
switch (relocation.getType()) {
|
||||
case ARM_ElfRelocationConstants.R_ARM_NONE:
|
||||
case ARM_ElfRelocationConstants.R_ARM_ABS32:
|
||||
case ARM_ElfRelocationConstants.R_ARM_REL32:
|
||||
case ARM_ElfRelocationConstants.R_ARM_GLOB_DAT:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_JUMP_SLOT:
|
||||
case ARM_ElfRelocationConstants.R_ARM_RELATIVE:
|
||||
case ARM_ElfRelocationConstants.R_ARM_GOT_PLT32:
|
||||
case ARM_ElfRelocationConstants.R_ARM_CALL:
|
||||
case ARM_ElfRelocationConstants.R_ARM_JUMP24:
|
||||
case ARM_ElfRelocationConstants.R_ARM_THM_JUMP24:
|
||||
return process32BitRelocation(program, relocation, oldImageBase, newImageBase);
|
||||
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PC24:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDR_PC_G0:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ABS16:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ABS12:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_ABS5:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ABS_8:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_SBREL32:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_CALL:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_PC8:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_BREL_ADJ:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_DESC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_SWI8:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_XPC25:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_XPC22:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_DTPMOD32:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_DTPOFF32:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_TPOFF32:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_COPY:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_GOTOFF32:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_BASE_PREL:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_GOT_BREL:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_BASE_ABS:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_PCREL_7_0:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_PCREL_15_8:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_PCREL_23_15:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDR_SBREL_11_0_NC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_SBREL_19_12_NC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_SBREL_27_20_CK:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TARGET1:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_SBREL31:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_V4BX:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TARGET2:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PREL31:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_MOVW_ABS_NC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_MOVT_ABS:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_MOVW_PREL_NC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_MOVT_PREL:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_MOVW_ABS_NC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_MOVT_ABS:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_MOVW_PREL_NC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_MOVT_PREL:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_JUMP19:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_JUMP6:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_ALU_PREL_11_0:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_PC12:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ABS32_NOI:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_REL32_NOI:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_PC_G0_NC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_PC_G0:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_PC_G1_NC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_PC_G1:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_PC_G2:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDR_PC_G1:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDR_PC_G2:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDRS_PC_G0:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDRS_PC_G1:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDRS_PC_G2:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDC_PC_G0:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDC_PC_G1:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDC_PC_G2:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_SB_G0_NC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_SB_G0:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_SB_G1_NC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_SB_G1:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_ALU_SB_G2:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDR_SB_G0:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDR_SB_G1:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDR_SB_G2:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDRS_SB_G0:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDRS_SB_G1:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDRS_SB_G2:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDC_SB_G0:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDC_SB_G1:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_LDC_SB_G2:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_MOVW_BREL_NC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_MOVT_BREL:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_MOVW_BREL:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_MOVW_BREL_NC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_MOVT_BREL:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_MOVW_BREL:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_GOTDESC:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_CALL:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_DESCSEQ:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_TLS_CALL:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PLT32_ABS:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_GOT_ABS:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_GOT_PREL:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_GOT_BREL12:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_GOTOFF12:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_GOTRELAX:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_GNU_VTENTRY:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_GNU_VTINHERIT:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_JUMP11:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_JUMP8:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_GD32:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_LDM32:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_LDO32:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_IE32:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_LE32:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_LDO12:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_LE12:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_TLS_IE12GP:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_0:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_1:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_2:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_3:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_4:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_5:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_6:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_7:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_8:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_9:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_10:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_11:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_12:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_13:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_14:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_PRIVATE_15:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_TLS_DESCSEQ16:
|
||||
// case ARM_ElfRelocationConstants.R_ARM_THM_TLS_DESCSEQ32:
|
||||
// return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlesProgram(Program program) {
|
||||
if (!ElfLoader.ELF_NAME.equals(program.getExecutableFormat())) {
|
||||
return false;
|
||||
}
|
||||
Language language = program.getLanguage();
|
||||
if (language.getLanguageDescription().getSize() != 32) {
|
||||
return false;
|
||||
}
|
||||
Processor processor = language.getProcessor();
|
||||
return (processor.equals(Processor.findOrPossiblyCreateProcessor("ARM")));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.emulation;
|
||||
|
||||
import ghidra.pcode.emulate.Emulate;
|
||||
import ghidra.pcode.emulate.EmulateInstructionStateModifier;
|
||||
import ghidra.pcode.emulate.callother.CountLeadingZerosOpBehavior;
|
||||
import ghidra.pcode.error.LowlevelError;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class ARMEmulateInstructionStateModifier extends EmulateInstructionStateModifier {
|
||||
|
||||
private Register TModeReg;
|
||||
private Register TBreg;
|
||||
private RegisterValue tMode;
|
||||
private RegisterValue aMode;
|
||||
|
||||
public ARMEmulateInstructionStateModifier(Emulate emu) {
|
||||
super(emu);
|
||||
TModeReg = language.getRegister("TMode");
|
||||
TBreg = language.getRegister("ISAModeSwitch");
|
||||
if (TModeReg != null && TBreg == null) {
|
||||
throw new RuntimeException("Expected language " + language.getLanguageID() +
|
||||
" to have TB register defined");
|
||||
}
|
||||
tMode = new RegisterValue(TModeReg, BigInteger.ONE);
|
||||
aMode = new RegisterValue(TModeReg, BigInteger.ZERO);
|
||||
|
||||
registerPcodeOpBehavior("count_leading_zeroes", new CountLeadingZerosOpBehavior());
|
||||
|
||||
/**
|
||||
* We could registerPcodeOpBehavior for one or more of the following pcodeop's:
|
||||
*
|
||||
Absolute
|
||||
ClearExclusiveLocal
|
||||
DataMemoryBarrier
|
||||
DataSynchronizationBarrier
|
||||
ExclusiveAccess
|
||||
HintDebug
|
||||
HintPreloadData
|
||||
HintPreloadDataForWrite
|
||||
HintPreloadInstruction
|
||||
HintYield
|
||||
IndexCheck
|
||||
InstructionSynchronizationBarrier
|
||||
ReverseBitOrder
|
||||
SendEvent
|
||||
SignedDoesSaturate
|
||||
SignedSaturate
|
||||
UnsignedDoesSaturate
|
||||
UnsignedSaturate
|
||||
WaitForEvent
|
||||
WaitForInterrupt
|
||||
coprocessor_function
|
||||
coprocessor_function2
|
||||
coprocessor_load
|
||||
coprocessor_load2
|
||||
coprocessor_loadlong
|
||||
coprocessor_loadlong2
|
||||
coprocessor_movefrom
|
||||
coprocessor_movefrom2
|
||||
coprocessor_moveto
|
||||
coprocessor_moveto2
|
||||
coprocessor_store
|
||||
coprocessor_store2
|
||||
coprocessor_storelong
|
||||
coprocessor_storelong2
|
||||
count_leading_zeroes
|
||||
disableDataAbortInterrupts
|
||||
disableFIQinterrupts
|
||||
disableIRQinterrupts
|
||||
enableDataAbortInterrupts
|
||||
enableFIQinterrupts
|
||||
enableIRQinterrupts
|
||||
hasExclusiveAccess
|
||||
isCurrentModePrivileged
|
||||
isFIQinterruptsEnabled
|
||||
isIRQinterruptsEnabled
|
||||
isThreadMode
|
||||
jazelle_branch
|
||||
setAbortMode
|
||||
setFIQMode
|
||||
setIRQMode
|
||||
setSupervisorMode
|
||||
setSystemMode
|
||||
setThreadModePrivileged
|
||||
setUndefinedMode
|
||||
setUserMode
|
||||
software_breakpoint
|
||||
software_interrupt
|
||||
*
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize TB register based upon context-register state before first instruction is executed.
|
||||
*/
|
||||
@Override
|
||||
public void initialExecuteCallback(Emulate emulate, Address current_address, RegisterValue contextRegisterValue) throws LowlevelError {
|
||||
BigInteger tModeValue = BigInteger.ZERO;
|
||||
if (contextRegisterValue != null) {
|
||||
tModeValue = contextRegisterValue.getRegisterValue(TModeReg).getUnsignedValueIgnoreMask();
|
||||
}
|
||||
if (!BigInteger.ZERO.equals(tModeValue)) {
|
||||
tModeValue = BigInteger.ONE;
|
||||
}
|
||||
emu.getMemoryState().setValue(TBreg, tModeValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle odd addresses which may occur when jumping/returning indirectly
|
||||
* to Thumb mode. It is assumed that language will properly handle
|
||||
* context changes during the flow of execution, we need only fix
|
||||
* the current program counter.
|
||||
*/
|
||||
@Override
|
||||
public void postExecuteCallback(Emulate emulate, Address lastExecuteAddress,
|
||||
PcodeOp[] lastExecutePcode, int lastPcodeIndex, Address currentAddress)
|
||||
throws LowlevelError {
|
||||
if (TModeReg == null) {
|
||||
return;
|
||||
}
|
||||
if (lastPcodeIndex < 0) {
|
||||
// ignore fall-through condition
|
||||
return;
|
||||
}
|
||||
int lastOp = lastExecutePcode[lastPcodeIndex].getOpcode();
|
||||
if (lastOp != PcodeOp.BRANCH && lastOp != PcodeOp.CBRANCH && lastOp != PcodeOp.BRANCHIND &&
|
||||
lastOp != PcodeOp.CALL && lastOp != PcodeOp.CALLIND && lastOp != PcodeOp.RETURN) {
|
||||
// only concerned with Branch, Call or Return ops
|
||||
return;
|
||||
}
|
||||
long tbValue = emu.getMemoryState().getValue(TBreg);
|
||||
if (tbValue == 1) {
|
||||
// Thumb mode
|
||||
emu.setContextRegisterValue(tMode); // change context to be consistent with TB value
|
||||
if ((currentAddress.getOffset() & 0x1) == 1) {
|
||||
emulate.setExecuteAddress(currentAddress.previous());
|
||||
}
|
||||
}
|
||||
else if (tbValue == 0) {
|
||||
|
||||
if ((currentAddress.getOffset() & 0x1) == 1) {
|
||||
throw new LowlevelError(
|
||||
"Flow to odd address occured without setting TB register (Thumb mode)");
|
||||
}
|
||||
|
||||
// ARM mode
|
||||
emu.setContextRegisterValue(aMode); // change context to be consistent with TB value
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.analysis;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
|
||||
/**
|
||||
* Test the changing of the ARM/Thumb bit for code flow
|
||||
*
|
||||
* ARM code:
|
||||
* ADR r12=addr
|
||||
* bx r12
|
||||
* addr:
|
||||
* Thumb code...
|
||||
*
|
||||
* Also tests that analysis puts on the correct reference on the r12, and not on the BX
|
||||
*/
|
||||
public class ArmThumbChangeDisassemblyTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
private TestEnv env;
|
||||
|
||||
private Program program;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
env = new TestEnv();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (program != null)
|
||||
env.release(program);
|
||||
program = null;
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
protected void setAnalysisOptions(String optionName) {
|
||||
int txId = program.startTransaction("Analyze");
|
||||
Options analysisOptions = program.getOptions(Program.ANALYSIS_PROPERTIES);
|
||||
analysisOptions.setBoolean(optionName, false);
|
||||
program.endTransaction(txId, true);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCorrectDisassembly() throws Exception {
|
||||
ProgramBuilder programBuilder = new ProgramBuilder("Test", ProgramBuilder._ARM);
|
||||
program = programBuilder.getProgram();
|
||||
int txId = program.startTransaction("Add Memory");// leave open until tearDown
|
||||
programBuilder.createMemory(".text", "1000", 64).setExecute(true);// initialized
|
||||
programBuilder.setBytes("1000", "ff ff ff ea 01 c0 8f e2 1c ff 2f e1 82 08 30 b5 70 47");
|
||||
|
||||
programBuilder.disassemble("1000", 11, true);
|
||||
programBuilder.analyze();
|
||||
|
||||
// should disassemble as ARM, then transition to Thumb
|
||||
Address instrAddr = programBuilder.addr("100c");
|
||||
Instruction instructionAt = program.getListing().getInstructionAt(instrAddr);
|
||||
Assert.assertNotEquals(null,instructionAt);
|
||||
|
||||
assertEquals(6, program.getListing().getNumInstructions());
|
||||
|
||||
RegisterValue registerValue = program.getProgramContext().getRegisterValue(program.getRegister("TMode"), instrAddr);
|
||||
|
||||
assertEquals(1,registerValue.getUnsignedValue().intValue());
|
||||
|
||||
// make sure reference put on operand 0, not mnemonic
|
||||
instrAddr = programBuilder.addr("1008");
|
||||
instructionAt = program.getListing().getInstructionAt(instrAddr);
|
||||
Reference[] operandReferences = instructionAt.getOperandReferences(0);
|
||||
assertEquals(1,operandReferences.length);
|
||||
assertEquals(0x100cL, operandReferences[0].getToAddress().getOffset());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue