GP-38 add semi-case-insensitive register get-by-name to Language and

construct various register context implementations with language instead
of register array to avoid need to build name loopup maps.
This commit is contained in:
ghidra1 2020-07-18 14:01:46 -04:00
parent 58157a1175
commit af58f51519
12 changed files with 88 additions and 102 deletions

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,14 +15,12 @@
*/
package ghidra.pcode.emulate;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*;
import ghidra.program.util.ProgramContextImpl;
import java.math.BigInteger;
import java.util.*;
import java.lang.UnsupportedOperationException;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*;
import ghidra.program.util.ProgramContextImpl;
public class EmulateDisassemblerContext implements DisassemblerContext {
@ -72,7 +69,7 @@ public class EmulateDisassemblerContext implements DisassemblerContext {
contextRegValue = null;
}
if (contextRegValue == null) {
ProgramContextImpl defaultContext = new ProgramContextImpl(language.getRegisters());
ProgramContextImpl defaultContext = new ProgramContextImpl(language);
language.applyContextSettings(defaultContext);
contextRegValue = defaultContext.getDefaultValue(contextReg, addr);
if (contextRegValue == null) {

View file

@ -49,8 +49,7 @@ public class ProgramRegisterContextDB extends AbstractStoredProgramContext imple
public ProgramRegisterContextDB(DBHandle dbHandle, ErrorHandler errHandler, Language lang,
CompilerSpec compilerSpec, AddressMap addrMap, Lock lock, int openMode,
CodeManager codeMgr, TaskMonitor monitor) throws VersionException, CancelledException {
super(lang.getRegisters());
super(lang);
this.addrMap = addrMap;
this.dbHandle = dbHandle;
this.errorHandler = errHandler;
@ -194,6 +193,9 @@ public class ProgramRegisterContextDB extends AbstractStoredProgramContext imple
@Override
public void setProgram(ProgramDB program) {
this.program = program;
if (program.getLanguage() != language) {
throw new IllegalArgumentException("Program does not correspond to current language");
}
}
@Override
@ -306,16 +308,16 @@ public class ProgramRegisterContextDB extends AbstractStoredProgramContext imple
AddressSetView programMemory, TaskMonitor monitor) throws CancelledException {
if (translator == null) {
Language lang = program.getLanguage();
// Language instance unchanged
boolean clearContext = Boolean.valueOf(
lang.getProperty(GhidraLanguagePropertyKeys.RESET_CONTEXT_ON_UPGRADE));
language.getProperty(GhidraLanguagePropertyKeys.RESET_CONTEXT_ON_UPGRADE));
if (clearContext) {
RegisterValueStore store = registerValueMap.get(baseContextRegister);
if (store != null) {
store.clearAll();
}
}
initializeDefaultValues(lang, newCompilerSpec);
initializeDefaultValues(language, newCompilerSpec);
return;
}
@ -350,11 +352,9 @@ public class ProgramRegisterContextDB extends AbstractStoredProgramContext imple
registerValueMap.remove(register);
}
initializeDefaultValues(newLanguage, newCompilerSpec);
init(newLanguage);
registers = newLanguage.getRegisters();
baseContextRegister = newLanguage.getContextBaseRegister();
init();
initializeDefaultValues(newLanguage, newCompilerSpec);
registerValueMap.clear();
initializedCurrentValues();

View file

@ -284,7 +284,7 @@ public class Disassembler implements DisassemblerConflictHandler {
bmMgr = program.getBookmarkManager();
}
else {
defaultLanguageContext = new ProgramContextImpl(language.getRegisters());
defaultLanguageContext = new ProgramContextImpl(language);
language.applyContextSettings(defaultLanguageContext);
}
@ -1496,11 +1496,6 @@ public class Disassembler implements DisassemblerConflictHandler {
}
}
private static Register[] getContextRegisters(Register baseContextRegister) {
return baseContextRegister != null ? new Register[] { baseContextRegister }
: new Register[0];
}
/**
* <code>DisassemblerProgramContext</code> is used as a proxy program context due to the
* delayed nature of laying down instructions and their associated context state.
@ -1517,7 +1512,7 @@ public class Disassembler implements DisassemblerConflictHandler {
private InstructionContext instructionContextCache = null;
DisassemblerProgramContext() {
super(getContextRegisters(Disassembler.this.baseContextRegister));
super(Disassembler.this.language); // getContextRegisters(Disassembler.this.baseContextRegister));
if (realProgramContext != null) {
setDefaultDisassemblyContext(realProgramContext.getDefaultDisassemblyContext());
}

View file

@ -18,6 +18,7 @@ package ghidra.program.model.lang;
import java.util.*;
import ghidra.program.model.address.Address;
import ghidra.util.Msg;
public class RegisterBuilder {
@ -44,14 +45,18 @@ public class RegisterBuilder {
}
public void addRegister(Register register) {
Register aliasedReg = null;
String name = register.getName();
if (registerMap.get(name) != null) {
Msg.error(this, "Duplicate register name: " + name);
// TODO: should we throw exception - hopefully sleigh will prevent this condition
}
for (Register reg : registerList) {
if (reg.getAddress().equals(register.getAddress()) &&
reg.getLeastSignificantBit() == register.getLeastSignificantBit() &&
reg.getBitLength() == register.getBitLength()) {
// define as register alias
reg.addAlias(register.getName());
registerMap.put(register.getName(), reg);
reg.addAlias(name);
addRegisterToMap(register);
return;
}
}
@ -59,7 +64,14 @@ public class RegisterBuilder {
contextAddress = register.getAddress();
}
registerList.add(register);
registerMap.put(register.getName(), register);
addRegisterToMap(register);
}
private void addRegisterToMap(Register register) {
String name = register.getName();
registerMap.put(name, register);
registerMap.put(name.toLowerCase(), register);
registerMap.put(name.toUpperCase(), register);
}
/**

View file

@ -132,7 +132,8 @@ public class RegisterManager {
}
/**
* Returns context base register or null if one has not been defined by the language.
* Get context base-register
* @return context base register or null if one has not been defined by the language.
*/
public Register getContextBaseRegister() {
return contextBaseRegister;
@ -140,9 +141,8 @@ public class RegisterManager {
/**
* Returns the largest register located at the specified address
*
* @param addr
* @return largest register
* @param addr register address
* @return register or null if not found
*/
public Register getRegister(Address addr) {
if (!addr.isRegisterAddress() && !addr.getAddressSpace().hasMappedRegisters()) {
@ -154,8 +154,8 @@ public class RegisterManager {
/**
* Returns all registers located at the specified address
*
* @param addr
* @return largest register
* @param addr register address
* @return array of registers found (may be empty)
*/
public Register[] getRegisters(Address addr) {
if (addr.isRegisterAddress() || addr.getAddressSpace().hasMappedRegisters()) {
@ -177,12 +177,10 @@ public class RegisterManager {
}
/**
* Returns the smallest register at the specified address whose size is
* greater than or equal the specified size.
* Get register by address and size
* @param addr register address
* @param size the size of the register (in bytes). A value of 0 will return the
* largest register at the specified addr
* @return register
* @param size register size
* @return register or null if not found
*/
public Register getRegister(Address addr, int size) {
if (!addr.isRegisterAddress() && !addr.getAddressSpace().hasMappedRegisters()) {
@ -192,24 +190,29 @@ public class RegisterManager {
}
/**
* Returns the register with the given name;
* @param name the name of the register to retrieve
* Get register by name. A semi-case-insensitive lookup is performed.
* The specified name must match either the case-sensitive name or
* be entirely lowercase or uppercase.
* @param name register name
* @return register or null if not found
*/
public Register getRegister(String name) {
return registerNameMap.get(name);
}
/**
* Return array of all registers.
* @return all registers
* Get all registers. Array returned is not protected and must not be modified
* by caller.
* @return array of all registers defined
*/
public Register[] getRegisters() {
return registers;
}
/**
* Returns the array of vector registers, sorted first by size and then by offset
* @return sorted vector registers
* Get all vector registers indentified by processor specification
* in sorted order based upon address and size.
* @return all vector registers
*/
public Register[] getSortedVectorRegisters() {
if (sortedVectorRegisters == null) {

View file

@ -561,6 +561,12 @@ public class SymbolUtilities {
* @param name the dynamic label name to parse into an address.
*/
public static Address parseDynamicName(AddressFactory factory, String name) {
// strip leading underscores
while (name.startsWith(UNDERSCORE)) {
name = name.substring(1);
}
String[] pieces = name.split(UNDERSCORE);
if (pieces.length < 2) { // if we have less than two pieces, then this is not a dynamic name.
return null;

View file

@ -18,16 +18,15 @@ package ghidra.program.util;
import java.util.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.DefaultProgramContext;
import ghidra.program.model.listing.ProgramContext;
abstract public class AbstractProgramContext implements ProgramContext, DefaultProgramContext {
protected Language language;
protected Register[] registers;
protected Register baseContextRegister;
private Map<String, Register> registerNameMap; // lazy initialized only when getRegister(name) called
private boolean hasNonFlowingContext = false;
private byte[] nonFlowingContextRegisterMask;
@ -35,17 +34,8 @@ abstract public class AbstractProgramContext implements ProgramContext, DefaultP
protected RegisterValue defaultDisassemblyContext;
protected AbstractProgramContext(Register[] registers) {
this.registers = registers;
init();
if (baseContextRegister != null) {
nonFlowingContextRegisterMask = baseContextRegister.getBaseMask().clone();
Arrays.fill(nonFlowingContextRegisterMask, (byte) 0);
flowingContextRegisterMask = nonFlowingContextRegisterMask.clone();
initContextBitMasks(baseContextRegister);
}
protected AbstractProgramContext(Language language) {
init(language);
}
/**
@ -109,37 +99,22 @@ abstract public class AbstractProgramContext implements ProgramContext, DefaultP
return value.clearBitValues(flowingContextRegisterMask);
}
protected void init() {
registerNameMap = null;
baseContextRegister = null;
for (Register register : registers) {
if (register.isProcessorContext()) {
baseContextRegister = register.getBaseRegister();
break; // should only be one
}
}
protected void init(Language language) {
this.language = language;
this.registers = language.getRegisters();
baseContextRegister = language.getContextBaseRegister();
if (baseContextRegister == null) {
baseContextRegister =
new Register("DEFAULT_CONTEXT", "DEFAULT_CONTEXT", Address.NO_ADDRESS, 4, true, 0);
}
defaultDisassemblyContext = new RegisterValue(baseContextRegister);
}
private Map<String, Register> getRegisterNameMap() {
if (registerNameMap != null) {
return registerNameMap;
}
// if register map hasn't been initialized, initialize it
registerNameMap = new HashMap<String, Register>();
// NOTE: if you want upper case names recognized, override this method and add them
for (Register register : registers) {
registerNameMap.put(register.getName(), register);
for (String alias : register.getAliases()) {
registerNameMap.put(alias, register);
}
if (baseContextRegister != null) {
nonFlowingContextRegisterMask = baseContextRegister.getBaseMask().clone();
Arrays.fill(nonFlowingContextRegisterMask, (byte) 0);
flowingContextRegisterMask = nonFlowingContextRegisterMask.clone();
initContextBitMasks(baseContextRegister);
}
return registerNameMap;
}
@Override
@ -155,7 +130,7 @@ abstract public class AbstractProgramContext implements ProgramContext, DefaultP
@Override
public final Register getRegister(String name) {
return getRegisterNameMap().get(name);
return language.getRegister(name);
}
@Override

View file

@ -20,8 +20,7 @@ import java.util.*;
import ghidra.program.database.register.InMemoryRangeMapAdapter;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.ContextChangeException;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
@ -34,8 +33,8 @@ abstract public class AbstractStoredProgramContext extends AbstractProgramContex
private Set<Register> registersWithValues; // cached set, recomputed whenever null
protected AbstractStoredProgramContext(Register[] registers) {
super(registers);
protected AbstractStoredProgramContext(Language language) {
super(language);
registerValueMap = new HashMap<>();
defaultRegisterValueMap = new HashMap<>();
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +16,7 @@
package ghidra.program.util;
import ghidra.program.database.register.InMemoryRangeMapAdapter;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register;
/**
@ -24,11 +24,11 @@ import ghidra.program.model.lang.Register;
*/
public class ProgramContextImpl extends AbstractStoredProgramContext {
/**
* Construct a new program context with the indicated registers.
* @param regs array of handles to registers
* Construct a new program context
* @param language program language
*/
public ProgramContextImpl(Register[] regs) {
super(regs);
public ProgramContextImpl(Language language) {
super(language);
}
@Override