Candidate release of source code.

This commit is contained in:
Dan 2019-03-26 13:45:32 -04:00
parent db81e6b3b0
commit 79d8f164f8
12449 changed files with 2800756 additions and 16 deletions

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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.
// }
}

View file

@ -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;
}

View file

@ -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
}
}

View file

@ -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;
}
}

View file

@ -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")));
}
}

View file

@ -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
}
}
}

View file

@ -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());
}
}