mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
1018 lines
36 KiB
C++
1018 lines
36 KiB
C++
/* ###
|
|
* 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.
|
|
*/
|
|
#include "translate.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
AttributeId ATTRIB_CODE = AttributeId("code",43);
|
|
AttributeId ATTRIB_CONTAIN = AttributeId("contain",44);
|
|
AttributeId ATTRIB_DEFAULTSPACE = AttributeId("defaultspace",45);
|
|
AttributeId ATTRIB_UNIQBASE = AttributeId("uniqbase",46);
|
|
|
|
ElementId ELEM_OP = ElementId("op",27);
|
|
ElementId ELEM_SLEIGH = ElementId("sleigh",28);
|
|
ElementId ELEM_SPACE = ElementId("space",29);
|
|
ElementId ELEM_SPACEID = ElementId("spaceid",30);
|
|
ElementId ELEM_SPACES = ElementId("spaces",31);
|
|
ElementId ELEM_SPACE_BASE = ElementId("space_base",32);
|
|
ElementId ELEM_SPACE_OTHER = ElementId("space_other",33);
|
|
ElementId ELEM_SPACE_OVERLAY = ElementId("space_overlay",34);
|
|
ElementId ELEM_SPACE_UNIQUE = ElementId("space_unique",35);
|
|
ElementId ELEM_TRUNCATE_SPACE = ElementId("truncate_space",36);
|
|
|
|
/// Parse a \<truncate_space> element to configure \b this object
|
|
/// \param decoder is the stream decoder
|
|
void TruncationTag::decode(Decoder &decoder)
|
|
|
|
{
|
|
uint4 elemId = decoder.openElement(ELEM_TRUNCATE_SPACE);
|
|
spaceName = decoder.readString(ATTRIB_SPACE);
|
|
size = decoder.readUnsignedInteger(ATTRIB_SIZE);
|
|
decoder.closeElement(elemId);
|
|
}
|
|
|
|
/// Construct a virtual space. This is usually used for the stack
|
|
/// space, which is indicated by the \b isFormal parameters, but multiple such spaces are allowed.
|
|
/// \param m is the manager for this \b program \b specific address space
|
|
/// \param t is associated processor translator
|
|
/// \param nm is the name of the space
|
|
/// \param ind is the integer identifier
|
|
/// \param sz is the size of the space
|
|
/// \param base is the containing space
|
|
/// \param dl is the heritage delay
|
|
/// \param isFormal is the formal stack space indicator
|
|
SpacebaseSpace::SpacebaseSpace(AddrSpaceManager *m,const Translate *t,const string &nm,int4 ind,int4 sz,
|
|
AddrSpace *base,int4 dl,bool isFormal)
|
|
: AddrSpace(m,t,IPTR_SPACEBASE,nm,t->isBigEndian(),sz,base->getWordSize(),ind,0,dl,dl)
|
|
{
|
|
contain = base;
|
|
hasbaseregister = false; // No base register assigned yet
|
|
isNegativeStack = true; // default stack growth
|
|
if (isFormal)
|
|
setFlags(formal_stackspace);
|
|
}
|
|
|
|
/// This is a partial constructor, which must be followed up
|
|
/// with decode in order to fillin the rest of the spaces
|
|
/// attributes
|
|
/// \param m is the associated address space manager
|
|
/// \param t is the associated processor translator
|
|
SpacebaseSpace::SpacebaseSpace(AddrSpaceManager *m,const Translate *t)
|
|
: AddrSpace(m,t,IPTR_SPACEBASE)
|
|
{
|
|
hasbaseregister = false;
|
|
isNegativeStack = true;
|
|
setFlags(programspecific);
|
|
}
|
|
|
|
/// This routine sets the base register associated with this \b virtual space
|
|
/// It will throw an exception if something tries to set two (different) base registers
|
|
/// \param data is the location data for the base register
|
|
/// \param truncSize is the size of the space covered by the register
|
|
/// \param stackGrowth is \b true if the stack which this register manages grows in a negative direction
|
|
void SpacebaseSpace::setBaseRegister(const VarnodeData &data,int4 truncSize,bool stackGrowth)
|
|
|
|
{
|
|
if (hasbaseregister) {
|
|
if ((baseloc != data)||(isNegativeStack != stackGrowth))
|
|
throw LowlevelError("Attempt to assign more than one base register to space: "+getName());
|
|
}
|
|
hasbaseregister = true;
|
|
isNegativeStack = stackGrowth;
|
|
baseOrig = data;
|
|
baseloc = data;
|
|
if (truncSize != baseloc.size) {
|
|
if (baseloc.space->isBigEndian())
|
|
baseloc.offset += (baseloc.size - truncSize);
|
|
baseloc.size = truncSize;
|
|
}
|
|
}
|
|
|
|
int4 SpacebaseSpace::numSpacebase(void) const
|
|
|
|
{
|
|
return hasbaseregister ? 1 : 0;
|
|
}
|
|
|
|
const VarnodeData &SpacebaseSpace::getSpacebase(int4 i) const
|
|
|
|
{
|
|
if ((!hasbaseregister)||(i!=0))
|
|
throw LowlevelError("No base register specified for space: "+getName());
|
|
return baseloc;
|
|
}
|
|
|
|
const VarnodeData &SpacebaseSpace::getSpacebaseFull(int4 i) const
|
|
|
|
{
|
|
if ((!hasbaseregister)||(i!=0))
|
|
throw LowlevelError("No base register specified for space: "+getName());
|
|
return baseOrig;
|
|
}
|
|
|
|
void SpacebaseSpace::decode(Decoder &decoder)
|
|
|
|
{
|
|
uint4 elemId = decoder.openElement(ELEM_SPACE_BASE);
|
|
decodeBasicAttributes(decoder);
|
|
contain = decoder.readSpace(ATTRIB_CONTAIN);
|
|
decoder.closeElement(elemId);
|
|
}
|
|
|
|
/// The \e join space range maps to the underlying pieces in a natural endian aware way.
|
|
/// Given an offset in the range, figure out what address it is mapping to.
|
|
/// The particular piece is passed back as an index, and the Address is returned.
|
|
/// \param offset is the offset within \b this range to map
|
|
/// \param pos will hold the passed back piece index
|
|
/// \return the Address mapped to
|
|
Address JoinRecord::getEquivalentAddress(uintb offset,int4 &pos) const
|
|
|
|
{
|
|
if (offset < unified.offset)
|
|
return Address(); // offset comes before this range
|
|
int4 smallOff = (int4)(offset - unified.offset);
|
|
if (pieces[0].space->isBigEndian()) {
|
|
for(pos=0;pos<pieces.size();++pos) {
|
|
int4 pieceSize = pieces[pos].size;
|
|
if (smallOff < pieceSize)
|
|
break;
|
|
smallOff -= pieceSize;
|
|
}
|
|
if (pos == pieces.size())
|
|
return Address(); // offset comes after this range
|
|
}
|
|
else {
|
|
for (pos = pieces.size() - 1; pos >= 0; --pos) {
|
|
int4 pieceSize = pieces[pos].size;
|
|
if (smallOff < pieceSize)
|
|
break;
|
|
smallOff -= pieceSize;
|
|
}
|
|
if (pos < 0)
|
|
return Address(); // offset comes after this range
|
|
}
|
|
return Address(pieces[pos].space,pieces[pos].offset + smallOff);
|
|
}
|
|
|
|
/// Allow sorting on JoinRecords so that a collection of pieces can be quickly mapped to
|
|
/// its logical whole, specified with a join address
|
|
bool JoinRecord::operator<(const JoinRecord &op2) const
|
|
|
|
{
|
|
// Some joins may have same piece but different unified size (floating point)
|
|
if (unified.size != op2.unified.size) // Compare size first
|
|
return (unified.size < op2.unified.size);
|
|
// Lexigraphic sort on pieces
|
|
int4 i=0;
|
|
for(;;) {
|
|
if (pieces.size()==i) {
|
|
return (op2.pieces.size()>i); // If more pieces in op2, it is bigger (return true), if same number this==op2, return false
|
|
}
|
|
if (op2.pieces.size()==i) return false; // More pieces in -this-, so it is bigger, return false
|
|
if (pieces[i] != op2.pieces[i])
|
|
return (pieces[i] < op2.pieces[i]);
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
/// Assuming the given list of VarnodeData go from most significant to least significant,
|
|
/// merge any contiguous elements in the list. Varnodes that are not in the \e stack address space
|
|
/// are only merged if the resulting byte range has a formal register name.
|
|
/// \param seq is the given list of VarnodeData
|
|
/// \param trans is the language to use for register names
|
|
void JoinRecord::mergeSequence(vector<VarnodeData> &seq,const Translate *trans)
|
|
|
|
{
|
|
int4 i=1;
|
|
while(i<seq.size()) {
|
|
VarnodeData &hi(seq[i-1]);
|
|
VarnodeData &lo(seq[i]);
|
|
if (hi.isContiguous(lo))
|
|
break;
|
|
i += 1;
|
|
}
|
|
if (i >= seq.size()) return;
|
|
vector<VarnodeData> res;
|
|
i = 1;
|
|
res.push_back(seq.front());
|
|
bool lastIsInformal = false;
|
|
while(i<seq.size()) {
|
|
VarnodeData &hi(res.back());
|
|
VarnodeData &lo(seq[i]);
|
|
if (hi.isContiguous(lo)) {
|
|
hi.offset = hi.space->isBigEndian() ? hi.offset : lo.offset;
|
|
hi.size += lo.size;
|
|
if (hi.space->getType() != IPTR_SPACEBASE) {
|
|
lastIsInformal = trans->getExactRegisterName(hi.space, hi.offset, hi.size).size() == 0;
|
|
}
|
|
}
|
|
else {
|
|
if (lastIsInformal)
|
|
break;
|
|
res.push_back(lo);
|
|
}
|
|
i += 1;
|
|
}
|
|
if (lastIsInformal) // If the merge contains an informal register
|
|
return; // throw it out and keep the original sequence
|
|
seq = res;
|
|
}
|
|
|
|
/// Initialize manager containing no address spaces. All the cached space slots are set to null
|
|
AddrSpaceManager::AddrSpaceManager(void)
|
|
|
|
{
|
|
defaultcodespace = (AddrSpace *)0;
|
|
defaultdataspace = (AddrSpace *)0;
|
|
constantspace = (AddrSpace *)0;
|
|
iopspace = (AddrSpace *)0;
|
|
fspecspace = (AddrSpace *)0;
|
|
joinspace = (AddrSpace *)0;
|
|
stackspace = (AddrSpace *)0;
|
|
uniqspace = (AddrSpace *)0;
|
|
joinallocate = 0;
|
|
}
|
|
|
|
/// The initialization of address spaces is the same across all
|
|
/// variants of the Translate object. This routine initializes
|
|
/// a single address space from a decoder element. It knows
|
|
/// which class derived from AddrSpace to instantiate based on
|
|
/// the ElementId.
|
|
/// \param decoder is the stream decoder
|
|
/// \param trans is the translator object to be associated with the new space
|
|
/// \return a pointer to the initialized AddrSpace
|
|
AddrSpace *AddrSpaceManager::decodeSpace(Decoder &decoder,const Translate *trans)
|
|
|
|
{
|
|
uint4 elemId = decoder.peekElement();
|
|
AddrSpace *res;
|
|
if (elemId == ELEM_SPACE_BASE)
|
|
res = new SpacebaseSpace(this,trans);
|
|
else if (elemId == ELEM_SPACE_UNIQUE)
|
|
res = new UniqueSpace(this,trans);
|
|
else if (elemId == ELEM_SPACE_OTHER)
|
|
res = new OtherSpace(this,trans);
|
|
else if (elemId == ELEM_SPACE_OVERLAY)
|
|
res = new OverlaySpace(this,trans);
|
|
else
|
|
res = new AddrSpace(this,trans,IPTR_PROCESSOR);
|
|
|
|
res->decode(decoder);
|
|
return res;
|
|
}
|
|
|
|
/// This routine initializes (almost) all the address spaces used
|
|
/// for a particular processor by using a \b \<spaces\> element,
|
|
/// which contains child elements for the specific address spaces.
|
|
/// This also instantiates the builtin \e constant space. It
|
|
/// should probably also instantiate the \b iop, \b fspec, and \b join
|
|
/// spaces, but this is currently done by the Architecture class.
|
|
/// \param decoder is the stream decoder
|
|
/// \param trans is the processor translator to be associated with the spaces
|
|
void AddrSpaceManager::decodeSpaces(Decoder &decoder,const Translate *trans)
|
|
|
|
{
|
|
// The first space should always be the constant space
|
|
insertSpace(new ConstantSpace(this,trans));
|
|
|
|
uint4 elemId = decoder.openElement(ELEM_SPACES);
|
|
string defname = decoder.readString(ATTRIB_DEFAULTSPACE);
|
|
while(decoder.peekElement() != 0) {
|
|
AddrSpace *spc = decodeSpace(decoder,trans);
|
|
insertSpace(spc);
|
|
}
|
|
decoder.closeElement(elemId);
|
|
AddrSpace *spc = getSpaceByName(defname);
|
|
if (spc == (AddrSpace *)0)
|
|
throw LowlevelError("Bad 'defaultspace' attribute: "+defname);
|
|
setDefaultCodeSpace(spc->getIndex());
|
|
}
|
|
|
|
/// Once all the address spaces have been initialized, this routine
|
|
/// should be called once to establish the official \e default
|
|
/// space for the processor, via its index. Should only be
|
|
/// called during initialization.
|
|
/// \param index is the index of the desired default space
|
|
void AddrSpaceManager::setDefaultCodeSpace(int4 index)
|
|
|
|
{
|
|
if (defaultcodespace != (AddrSpace *)0)
|
|
throw LowlevelError("Default space set multiple times");
|
|
if (baselist.size()<=index || baselist[index] == (AddrSpace *)0)
|
|
throw LowlevelError("Bad index for default space");
|
|
defaultcodespace = baselist[index];
|
|
defaultdataspace = defaultcodespace; // By default the default data space is the same
|
|
}
|
|
|
|
/// If the architecture has different code and data spaces, this routine can be called
|
|
/// to set the \e data space after the \e code space has been set.
|
|
/// \param index is the index of the desired default space
|
|
void AddrSpaceManager::setDefaultDataSpace(int4 index)
|
|
|
|
{
|
|
if (defaultcodespace == (AddrSpace *)0)
|
|
throw LowlevelError("Default data space must be set after the code space");
|
|
if (baselist.size()<=index || baselist[index] == (AddrSpace *)0)
|
|
throw LowlevelError("Bad index for default data space");
|
|
defaultdataspace = baselist[index];
|
|
}
|
|
|
|
/// For spaces with alignment restrictions, the address of a small variable must be justified
|
|
/// within a larger aligned memory word, usually either to the left boundary for little endian encoding
|
|
/// or to the right boundary for big endian encoding. Some compilers justify small variables to
|
|
/// the opposite side of the one indicated by the endianness. Setting this property on a space
|
|
/// causes the decompiler to use this justification
|
|
void AddrSpaceManager::setReverseJustified(AddrSpace *spc)
|
|
|
|
{
|
|
spc->setFlags(AddrSpace::reverse_justification);
|
|
}
|
|
|
|
/// This adds a previously instantiated address space (AddrSpace)
|
|
/// to the model for this processor. It checks a set of
|
|
/// indexing and naming conventions for the space and throws
|
|
/// an exception if the conventions are violated. Should
|
|
/// only be called during initialization.
|
|
/// \todo This really shouldn't be public. Need to move the
|
|
/// allocation of \b iop, \b fspec, and \b join out of Architecture
|
|
/// \param spc the address space to insert
|
|
void AddrSpaceManager::insertSpace(AddrSpace *spc)
|
|
|
|
{
|
|
bool nameTypeMismatch = false;
|
|
bool duplicateName = false;
|
|
bool duplicateId = false;
|
|
switch(spc->getType()) {
|
|
case IPTR_CONSTANT:
|
|
if (spc->getName() != ConstantSpace::NAME)
|
|
nameTypeMismatch = true;
|
|
if (spc->index != ConstantSpace::INDEX)
|
|
throw LowlevelError("const space must be assigned index 0");
|
|
constantspace = spc;
|
|
break;
|
|
case IPTR_INTERNAL:
|
|
if (spc->getName() != UniqueSpace::NAME)
|
|
nameTypeMismatch = true;
|
|
if (uniqspace != (AddrSpace *)0)
|
|
duplicateName = true;
|
|
uniqspace = spc;
|
|
break;
|
|
case IPTR_FSPEC:
|
|
if (spc->getName() != "fspec")
|
|
nameTypeMismatch = true;
|
|
if (fspecspace != (AddrSpace *)0)
|
|
duplicateName = true;
|
|
fspecspace = spc;
|
|
break;
|
|
case IPTR_JOIN:
|
|
if (spc->getName() != JoinSpace::NAME)
|
|
nameTypeMismatch = true;
|
|
if (joinspace != (AddrSpace *)0)
|
|
duplicateName = true;
|
|
joinspace = spc;
|
|
break;
|
|
case IPTR_IOP:
|
|
if (spc->getName() != "iop")
|
|
nameTypeMismatch = true;
|
|
if (iopspace != (AddrSpace *)0)
|
|
duplicateName = true;
|
|
iopspace = spc;
|
|
break;
|
|
case IPTR_SPACEBASE:
|
|
if (spc->getName() == "stack") {
|
|
if (stackspace != (AddrSpace *)0)
|
|
duplicateName = true;
|
|
stackspace = spc;
|
|
}
|
|
// fallthru
|
|
case IPTR_PROCESSOR:
|
|
if (spc->isOverlay()) { // If this is a new overlay space
|
|
spc->getContain()->setFlags(AddrSpace::overlaybase); // Mark the base as being overlayed
|
|
}
|
|
else if (spc->isOtherSpace()) {
|
|
if (spc->index != OtherSpace::INDEX)
|
|
throw LowlevelError("OTHER space must be assigned index 1");
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (baselist.size() <= spc->index)
|
|
baselist.resize(spc->index+1, (AddrSpace *)0);
|
|
|
|
duplicateId = baselist[spc->index] != (AddrSpace *)0;
|
|
|
|
if (!nameTypeMismatch && !duplicateName && !duplicateId) {
|
|
duplicateName = !name2Space.insert(pair<string,AddrSpace *>(spc->getName(),spc)).second;
|
|
}
|
|
|
|
if (nameTypeMismatch || duplicateName || duplicateId) {
|
|
string errMsg = "Space " + spc->getName();
|
|
if (nameTypeMismatch)
|
|
errMsg = errMsg + " was initialized with wrong type";
|
|
if (duplicateName)
|
|
errMsg = errMsg + " was initialized more than once";
|
|
if (duplicateId)
|
|
errMsg = errMsg + " was assigned as id duplicating: "+baselist[spc->index]->getName();
|
|
if (spc->refcount == 0)
|
|
delete spc;
|
|
spc = (AddrSpace *)0;
|
|
throw LowlevelError(errMsg);
|
|
}
|
|
baselist[spc->index] = spc;
|
|
spc->refcount += 1;
|
|
assignShortcut(spc);
|
|
}
|
|
|
|
/// Different managers may need to share the same spaces. I.e. if different programs being
|
|
/// analyzed share the same processor. This routine pulls in a reference of every space in -op2-
|
|
/// in order to manage it from within -this-
|
|
/// \param op2 is a pointer to space manager being copied
|
|
void AddrSpaceManager::copySpaces(const AddrSpaceManager *op2)
|
|
|
|
{ // Insert every space in -op2- into -this- manager
|
|
for(int4 i=0;i<op2->baselist.size();++i) {
|
|
AddrSpace *spc = op2->baselist[i];
|
|
if (spc != (AddrSpace *)0)
|
|
insertSpace(spc);
|
|
}
|
|
setDefaultCodeSpace(op2->getDefaultCodeSpace()->getIndex());
|
|
setDefaultDataSpace(op2->getDefaultDataSpace()->getIndex());
|
|
}
|
|
|
|
/// Perform the \e privileged act of associating a base register with an existing \e virtual space
|
|
/// \param basespace is the virtual space
|
|
/// \param ptrdata is the location data for the base register
|
|
/// \param truncSize is the size of the space covered by the base register
|
|
/// \param stackGrowth is true if the stack grows "normally" towards address 0
|
|
void AddrSpaceManager::addSpacebasePointer(SpacebaseSpace *basespace,const VarnodeData &ptrdata,int4 truncSize,bool stackGrowth)
|
|
|
|
{
|
|
basespace->setBaseRegister(ptrdata,truncSize,stackGrowth);
|
|
}
|
|
|
|
/// Provide a new specialized resolver for a specific AddrSpace. The manager takes ownership of resolver.
|
|
/// \param spc is the space to which the resolver is associated
|
|
/// \param rsolv is the new resolver object
|
|
void AddrSpaceManager::insertResolver(AddrSpace *spc,AddressResolver *rsolv)
|
|
|
|
{
|
|
int4 ind = spc->getIndex();
|
|
while(resolvelist.size() <= ind)
|
|
resolvelist.push_back((AddressResolver *)0);
|
|
if (resolvelist[ind] != (AddressResolver *)0)
|
|
delete resolvelist[ind];
|
|
resolvelist[ind] = rsolv;
|
|
}
|
|
|
|
/// This method establishes for a single address space, what range of constants are checked
|
|
/// as possible symbol starts, when it is not known apriori that a constant is a pointer.
|
|
/// \param range is the range of values for a single address space
|
|
void AddrSpaceManager::setInferPtrBounds(const Range &range)
|
|
|
|
{
|
|
range.getSpace()->pointerLowerBound = range.getFirst();
|
|
range.getSpace()->pointerUpperBound = range.getLast();
|
|
}
|
|
|
|
/// Base destructor class, cleans up AddrSpace pointers which
|
|
/// must be explicited created via \e new
|
|
AddrSpaceManager::~AddrSpaceManager(void)
|
|
|
|
{
|
|
for(vector<AddrSpace *>::iterator iter=baselist.begin();iter!=baselist.end();++iter) {
|
|
AddrSpace *spc = *iter;
|
|
if (spc == (AddrSpace *)0) continue;
|
|
if (spc->refcount > 1)
|
|
spc->refcount -= 1;
|
|
else
|
|
delete spc;
|
|
}
|
|
for(int4 i=0;i<resolvelist.size();++i) {
|
|
if (resolvelist[i] != (AddressResolver *)0)
|
|
delete resolvelist[i];
|
|
}
|
|
for(int4 i=0;i<splitlist.size();++i)
|
|
delete splitlist[i]; // Delete any join records
|
|
}
|
|
|
|
/// Assign a \e shortcut character to the given address space.
|
|
/// This routine makes use of the desired type of the new space
|
|
/// and info about shortcuts for spaces that already exist to
|
|
/// pick a unique and consistent character. This method also builds
|
|
/// up a map from short to AddrSpace object.
|
|
/// \param spc is the given AddrSpace
|
|
void AddrSpaceManager::assignShortcut(AddrSpace *spc)
|
|
|
|
{
|
|
if (spc->shortcut != ' ') { // If the shortcut is already assigned
|
|
shortcut2Space.insert(pair<int4,AddrSpace *>(spc->shortcut,spc));
|
|
return;
|
|
}
|
|
char shortcut;
|
|
switch(spc->getType()) {
|
|
case IPTR_CONSTANT:
|
|
shortcut = '#';
|
|
break;
|
|
case IPTR_PROCESSOR:
|
|
if (spc->getName() == "register")
|
|
shortcut = '%';
|
|
else
|
|
shortcut = spc->getName()[0];
|
|
break;
|
|
case IPTR_SPACEBASE:
|
|
shortcut = 's';
|
|
break;
|
|
case IPTR_INTERNAL:
|
|
shortcut = 'u';
|
|
break;
|
|
case IPTR_FSPEC:
|
|
shortcut = 'f';
|
|
break;
|
|
case IPTR_JOIN:
|
|
shortcut = 'j';
|
|
break;
|
|
case IPTR_IOP:
|
|
shortcut = 'i';
|
|
break;
|
|
default:
|
|
shortcut = 'x';
|
|
break;
|
|
}
|
|
|
|
if (shortcut >= 'A' && shortcut <= 'Z')
|
|
shortcut += 0x20;
|
|
|
|
int4 collisionCount = 0;
|
|
while(!shortcut2Space.insert(pair<int4,AddrSpace *>(shortcut,spc)).second) {
|
|
collisionCount += 1;
|
|
if (collisionCount >26) {
|
|
// Could not find a unique shortcut, but we just re-use 'z' as we
|
|
// can always use the long form to specify the address if there are really so many
|
|
// spaces that need to be distinguishable (in the console mode)
|
|
spc->shortcut = 'z';
|
|
return;
|
|
}
|
|
shortcut += 1;
|
|
if (shortcut < 'a' || shortcut > 'z')
|
|
shortcut = 'a';
|
|
}
|
|
spc->shortcut = (char)shortcut;
|
|
}
|
|
|
|
/// \param spc is the AddrSpace to mark
|
|
/// \param size is the (minimum) size of a near pointer in bytes
|
|
void AddrSpaceManager::markNearPointers(AddrSpace *spc,int4 size)
|
|
|
|
{
|
|
spc->setFlags(AddrSpace::has_nearpointers);
|
|
if (spc->minimumPointerSize == 0 && spc->addressSize != size)
|
|
spc->minimumPointerSize = size;
|
|
}
|
|
|
|
/// All address spaces have a unique name associated with them.
|
|
/// This routine retrieves the AddrSpace object based on the
|
|
/// desired name.
|
|
/// \param nm is the name of the address space
|
|
/// \return a pointer to the AddrSpace object
|
|
AddrSpace *AddrSpaceManager::getSpaceByName(const string &nm) const
|
|
|
|
{
|
|
map<string,AddrSpace *>::const_iterator iter = name2Space.find(nm);
|
|
if (iter == name2Space.end())
|
|
return (AddrSpace *)0;
|
|
return (*iter).second;
|
|
}
|
|
|
|
/// All address spaces have a unique shortcut (ASCII) character
|
|
/// assigned to them. This routine retrieves an AddrSpace object
|
|
/// given a specific shortcut.
|
|
/// \param sc is the shortcut character
|
|
/// \return a pointer to an AddrSpace
|
|
AddrSpace *AddrSpaceManager::getSpaceByShortcut(char sc) const
|
|
|
|
{
|
|
map<int4,AddrSpace *>::const_iterator iter;
|
|
iter = shortcut2Space.find(sc);
|
|
if (iter == shortcut2Space.end())
|
|
return (AddrSpace *)0;
|
|
return (*iter).second;
|
|
}
|
|
|
|
/// \brief Resolve a native constant into an Address
|
|
///
|
|
/// If there is a special resolver for the AddrSpace, this is invoked, otherwise
|
|
/// basic wordsize conversion and wrapping is performed. If the address encoding is
|
|
/// partial (as in a \e near pointer) and the full encoding can be recovered, it is passed back.
|
|
/// The \e sz parameter indicates the number of bytes in constant and is used to determine if
|
|
/// the constant is a partial or full pointer encoding. A value of -1 indicates the value is
|
|
/// known to be a full encoding.
|
|
/// \param spc is the space to generate the address from
|
|
/// \param val is the constant encoding of the address
|
|
/// \param sz is the size of the constant encoding (or -1)
|
|
/// \param point is the context address (for recovering full encoding info if necessary)
|
|
/// \param fullEncoding is used to pass back the recovered full encoding of the pointer
|
|
/// \return the formal Address associated with the encoding
|
|
Address AddrSpaceManager::resolveConstant(AddrSpace *spc,uintb val,int4 sz,const Address &point,uintb &fullEncoding) const
|
|
|
|
{
|
|
int4 ind = spc->getIndex();
|
|
if (ind < resolvelist.size()) {
|
|
AddressResolver *resolve = resolvelist[ind];
|
|
if (resolve != (AddressResolver *)0)
|
|
return resolve->resolve(val,sz,point,fullEncoding);
|
|
}
|
|
fullEncoding = val;
|
|
val = AddrSpace::addressToByte(val,spc->getWordSize());
|
|
val = spc->wrapOffset(val);
|
|
return Address(spc,val);
|
|
}
|
|
|
|
/// Get the next space in the absolute order of addresses.
|
|
/// This ordering is determined by the AddrSpace index.
|
|
/// \param spc is the pointer to the space being queried
|
|
/// \return the pointer to the next space in absolute order
|
|
AddrSpace *AddrSpaceManager::getNextSpaceInOrder(AddrSpace *spc) const
|
|
{
|
|
if (spc == (AddrSpace *)0) {
|
|
return baselist[0];
|
|
}
|
|
if (spc == (AddrSpace *) ~((uintp)0)) {
|
|
return (AddrSpace *)0;
|
|
}
|
|
int4 index = spc->getIndex() + 1;
|
|
while (index < baselist.size()) {
|
|
AddrSpace *res = baselist[index];
|
|
if (res != (AddrSpace *)0)
|
|
return res;
|
|
index += 1;
|
|
}
|
|
return (AddrSpace *) ~((uintp)0);
|
|
}
|
|
|
|
/// Given a list of memory locations, the \e pieces, either find a pre-existing JoinRecord or
|
|
/// create a JoinRecord that represents the logical joining of the pieces. The pieces must
|
|
/// be in order from most significant to least significant.
|
|
/// \param pieces if the list memory locations to be joined
|
|
/// \param logicalsize of a \e single \e piece join, or zero
|
|
/// \return a pointer to the JoinRecord
|
|
JoinRecord *AddrSpaceManager::findAddJoin(const vector<VarnodeData> &pieces,uint4 logicalsize)
|
|
|
|
{ // Find a pre-existing split record, or create a new one corresponding to the input -pieces-
|
|
// If -logicalsize- is 0, calculate logical size as sum of pieces
|
|
if (pieces.size() == 0)
|
|
throw LowlevelError("Cannot create a join without pieces");
|
|
if ((pieces.size()==1)&&(logicalsize==0))
|
|
throw LowlevelError("Cannot create a single piece join without a logical size");
|
|
|
|
uint4 totalsize;
|
|
if (logicalsize != 0) {
|
|
if (pieces.size() != 1)
|
|
throw LowlevelError("Cannot specify logical size for multiple piece join");
|
|
totalsize = logicalsize;
|
|
}
|
|
else {
|
|
totalsize = 0;
|
|
for(int4 i=0;i<pieces.size();++i) // Calculate sum of the sizes of all pieces
|
|
totalsize += pieces[i].size;
|
|
if (totalsize == 0)
|
|
throw LowlevelError("Cannot create a zero size join");
|
|
}
|
|
|
|
JoinRecord testnode;
|
|
|
|
testnode.pieces = pieces;
|
|
testnode.unified.size = totalsize;
|
|
set<JoinRecord *,JoinRecordCompare>::const_iterator iter;
|
|
iter = splitset.find(&testnode);
|
|
if (iter != splitset.end()) // If already in the set
|
|
return *iter;
|
|
|
|
JoinRecord *newjoin = new JoinRecord();
|
|
newjoin->pieces = pieces;
|
|
|
|
uint4 roundsize = (totalsize + 15) & ~((uint4)0xf); // Next biggest multiple of 16
|
|
|
|
newjoin->unified.space = joinspace;
|
|
newjoin->unified.offset = joinallocate;
|
|
joinallocate += roundsize;
|
|
newjoin->unified.size = totalsize;
|
|
splitset.insert(newjoin);
|
|
splitlist.push_back(newjoin);
|
|
return splitlist.back();
|
|
}
|
|
|
|
/// Given a specific \e offset into the \e join address space, recover the JoinRecord that
|
|
/// contains the offset, as a range in the \e join address space. If there is no existing
|
|
/// record, null is returned.
|
|
/// \param offset is an offset into the join space
|
|
/// \return the JoinRecord containing that offset or null
|
|
JoinRecord *AddrSpaceManager::findJoinInternal(uintb offset) const
|
|
|
|
{
|
|
int4 min=0;
|
|
int4 max=splitlist.size()-1;
|
|
while(min<=max) { // Binary search
|
|
int4 mid = (min+max)/2;
|
|
JoinRecord *rec = splitlist[mid];
|
|
uintb val = rec->unified.offset;
|
|
if (val + rec->unified.size <= offset)
|
|
min = mid + 1;
|
|
else if (val > offset)
|
|
max = mid - 1;
|
|
else
|
|
return rec;
|
|
}
|
|
return (JoinRecord *)0;
|
|
}
|
|
|
|
/// Given a specific \e offset into the \e join address space, recover the JoinRecord that
|
|
/// lists the pieces corresponding to that offset. The offset must originally have come from
|
|
/// a JoinRecord returned by \b findAddJoin, otherwise this method throws an exception.
|
|
/// \param offset is an offset into the join space
|
|
/// \return the JoinRecord for that offset
|
|
JoinRecord *AddrSpaceManager::findJoin(uintb offset) const
|
|
|
|
{
|
|
int4 min=0;
|
|
int4 max=splitlist.size()-1;
|
|
while(min<=max) { // Binary search
|
|
int4 mid = (min+max)/2;
|
|
JoinRecord *rec = splitlist[mid];
|
|
uintb val = rec->unified.offset;
|
|
if (val == offset) return rec;
|
|
if (val < offset)
|
|
min = mid + 1;
|
|
else
|
|
max = mid - 1;
|
|
}
|
|
throw LowlevelError("Unlinked join address");
|
|
}
|
|
|
|
/// Set the number of passes for a specific AddrSpace before deadcode removal is allowed
|
|
/// for that space.
|
|
/// \param spc is the AddrSpace to change
|
|
/// \param delaydelta is the number of rounds to the delay should be set to
|
|
void AddrSpaceManager::setDeadcodeDelay(AddrSpace *spc,int4 delaydelta)
|
|
|
|
{
|
|
spc->deadcodedelay = delaydelta;
|
|
}
|
|
|
|
/// Mark the named space as truncated from its original size
|
|
/// \param tag is a description of the space and how it should be truncated
|
|
void AddrSpaceManager::truncateSpace(const TruncationTag &tag)
|
|
|
|
{
|
|
AddrSpace *spc = getSpaceByName(tag.getName());
|
|
if (spc == (AddrSpace *)0)
|
|
throw LowlevelError("Unknown space in <truncate_space> command: "+tag.getName());
|
|
spc->truncateSpace(tag.getSize());
|
|
}
|
|
|
|
/// This handles the situation where we need to find a logical address to hold the lower
|
|
/// precision floating-point value that is stored in a bigger register
|
|
/// If the logicalsize (precision) requested matches the -realsize- of the register
|
|
/// just return the real address. Otherwise construct a join address to hold the logical value
|
|
/// \param realaddr is the address of the real floating-point register
|
|
/// \param realsize is the size of the real floating-point register
|
|
/// \param logicalsize is the size (lower precision) size of the logical value
|
|
Address AddrSpaceManager::constructFloatExtensionAddress(const Address &realaddr,int4 realsize,
|
|
int4 logicalsize)
|
|
{
|
|
if (logicalsize == realsize)
|
|
return realaddr;
|
|
vector<VarnodeData> pieces;
|
|
pieces.emplace_back();
|
|
pieces.back().space = realaddr.getSpace();
|
|
pieces.back().offset = realaddr.getOffset();
|
|
pieces.back().size = realsize;
|
|
|
|
JoinRecord *join = findAddJoin(pieces,logicalsize);
|
|
return join->getUnified().getAddr();
|
|
}
|
|
|
|
/// This handles the common case, of trying to find a join address given a high location and a low
|
|
/// location. This may not return an address in the \e join address space. It checks for the case
|
|
/// where the two pieces are contiguous locations in a mappable space, in which case it just returns
|
|
/// the containing address
|
|
/// \param translate is the Translate object used to find registers
|
|
/// \param hiaddr is the address of the most significant piece to be joined
|
|
/// \param hisz is the size of the most significant piece
|
|
/// \param loaddr is the address of the least significant piece
|
|
/// \param losz is the size of the least significant piece
|
|
/// \return an address representing the start of the joined range
|
|
Address AddrSpaceManager::constructJoinAddress(const Translate *translate,
|
|
const Address &hiaddr,int4 hisz,
|
|
const Address &loaddr,int4 losz)
|
|
{
|
|
spacetype hitp = hiaddr.getSpace()->getType();
|
|
spacetype lotp = loaddr.getSpace()->getType();
|
|
bool usejoinspace = true;
|
|
if (((hitp != IPTR_SPACEBASE)&&(hitp != IPTR_PROCESSOR))||
|
|
((lotp != IPTR_SPACEBASE)&&(lotp != IPTR_PROCESSOR)))
|
|
throw LowlevelError("Trying to join in appropriate locations");
|
|
if ((hitp == IPTR_SPACEBASE)||(lotp == IPTR_SPACEBASE)||
|
|
(hiaddr.getSpace() == getDefaultCodeSpace())||
|
|
(loaddr.getSpace() == getDefaultCodeSpace()))
|
|
usejoinspace = false;
|
|
if (hiaddr.isContiguous(hisz,loaddr,losz)) { // If we are contiguous
|
|
if (!usejoinspace) { // and in a mappable space, just return the earliest address
|
|
if (hiaddr.isBigEndian())
|
|
return hiaddr;
|
|
return loaddr;
|
|
}
|
|
else { // If we are in a non-mappable (register) space, check to see if a parent register exists
|
|
if (hiaddr.isBigEndian()) {
|
|
if (translate->getRegisterName(hiaddr.getSpace(),hiaddr.getOffset(),(hisz+losz)).size() != 0)
|
|
return hiaddr;
|
|
}
|
|
else {
|
|
if (translate->getRegisterName(loaddr.getSpace(),loaddr.getOffset(),(hisz+losz)).size() != 0)
|
|
return loaddr;
|
|
}
|
|
}
|
|
}
|
|
// Otherwise construct a formal JoinRecord
|
|
vector<VarnodeData> pieces;
|
|
pieces.emplace_back();
|
|
pieces.emplace_back();
|
|
pieces[0].space = hiaddr.getSpace();
|
|
pieces[0].offset = hiaddr.getOffset();
|
|
pieces[0].size = hisz;
|
|
pieces[1].space = loaddr.getSpace();
|
|
pieces[1].offset = loaddr.getOffset();
|
|
pieces[1].size = losz;
|
|
JoinRecord *join = findAddJoin(pieces,0);
|
|
return join->getUnified().getAddr();
|
|
}
|
|
|
|
/// If an Address in the \e join AddressSpace is shifted from its original offset, it may no
|
|
/// longer have a valid JoinRecord. The shift or size change may even make the address of
|
|
/// one of the pieces a more natural representation. Given a new Address and size, this method
|
|
/// decides if there is a matching JoinRecord. If not it either constructs a new JoinRecord or
|
|
/// computes the address within the containing piece. The given Address is changed if necessary
|
|
/// either to the offset corresponding to the new JoinRecord or to a normal \e non-join Address.
|
|
/// \param addr is the given Address
|
|
/// \param size is the size of the range in bytes
|
|
void AddrSpaceManager::renormalizeJoinAddress(Address &addr,int4 size)
|
|
|
|
{
|
|
JoinRecord *joinRecord = findJoinInternal(addr.getOffset());
|
|
if (joinRecord == (JoinRecord *)0)
|
|
throw LowlevelError("Join address not covered by a JoinRecord");
|
|
if (addr.getOffset() == joinRecord->unified.offset && size == joinRecord->unified.size)
|
|
return; // JoinRecord matches perfectly, no change necessary
|
|
int4 pos1;
|
|
Address addr1 = joinRecord->getEquivalentAddress(addr.getOffset(), pos1);
|
|
int4 pos2;
|
|
Address addr2 = joinRecord->getEquivalentAddress(addr.getOffset() + (size-1), pos2);
|
|
if (addr2.isInvalid())
|
|
throw LowlevelError("Join address range not covered");
|
|
if (pos1 == pos2) {
|
|
addr = addr1;
|
|
return;
|
|
}
|
|
vector<VarnodeData> newPieces;
|
|
int4 sizeTrunc1 = (int4)(addr1.getOffset() - joinRecord->pieces[pos1].offset);
|
|
int4 sizeTrunc2 = joinRecord->pieces[pos2].size - (int4)(addr2.getOffset() - joinRecord->pieces[pos2].offset) - 1;
|
|
|
|
if (pos2 < pos1) { // Little endian
|
|
newPieces.push_back(joinRecord->pieces[pos2]);
|
|
pos2 += 1;
|
|
while(pos2 <= pos1) {
|
|
newPieces.push_back(joinRecord->pieces[pos2]);
|
|
pos2 += 1;
|
|
}
|
|
newPieces.back().offset = addr1.getOffset();
|
|
newPieces.back().size -= sizeTrunc1;
|
|
newPieces.front().size -= sizeTrunc2;
|
|
}
|
|
else {
|
|
newPieces.push_back(joinRecord->pieces[pos1]);
|
|
pos1 += 1;
|
|
while(pos1 <= pos2) {
|
|
newPieces.push_back(joinRecord->pieces[pos1]);
|
|
pos1 += 1;
|
|
}
|
|
newPieces.front().offset = addr1.getOffset();
|
|
newPieces.front().size -= sizeTrunc1;
|
|
newPieces.back().size -= sizeTrunc2;
|
|
}
|
|
JoinRecord *newJoinRecord = findAddJoin(newPieces, 0);
|
|
addr = Address(newJoinRecord->unified.space,newJoinRecord->unified.offset);
|
|
}
|
|
|
|
/// The string \e must contain a hexadecimal offset. The offset may be optionally prepended with "0x".
|
|
/// The string may optionally start with the name of the address space to associate with the offset, followed
|
|
/// by ':' to separate it from the offset. If the name is not present, the default data space is assumed.
|
|
/// \param val is the string to parse
|
|
/// \return the parsed address
|
|
Address AddrSpaceManager::parseAddressSimple(const string &val)
|
|
|
|
{
|
|
string::size_type col = val.find(':');
|
|
AddrSpace *spc;
|
|
if (col==string::npos) {
|
|
spc = getDefaultDataSpace();
|
|
col = 0;
|
|
}
|
|
else {
|
|
string spcName = val.substr(0,col);
|
|
spc = getSpaceByName(spcName);
|
|
if (spc == (AddrSpace *)0)
|
|
throw LowlevelError("Unknown address space: " + spcName);
|
|
col += 1;
|
|
}
|
|
if (col + 2 <= val.size()) {
|
|
if (val[col] == '0' && val[col+1] == 'x')
|
|
col += 2;
|
|
}
|
|
istringstream s(val.substr(col));
|
|
uintb off;
|
|
s >> hex >> off;
|
|
return Address(spc,AddrSpace::addressToByte(off, spc->getWordSize()));
|
|
}
|
|
|
|
/// This constructs only a shell for the Translate object. It
|
|
/// won't be usable until it is initialized for a specific processor
|
|
/// The main entry point for this is the Translate::initialize method,
|
|
/// which must be overridden by a derived class
|
|
Translate::Translate(void)
|
|
|
|
{
|
|
target_isbigendian = false;
|
|
unique_base=0;
|
|
alignment = 1;
|
|
}
|
|
|
|
/// If no floating-point format objects were registered by the \b initialize method, this
|
|
/// method will fill in some suitable default formats. These defaults are based on
|
|
/// the 4-byte and 8-byte encoding specified by the IEEE 754 standard.
|
|
void Translate::setDefaultFloatFormats(void)
|
|
|
|
{
|
|
if (floatformats.empty()) { // Default IEEE 754 float formats
|
|
floatformats.push_back(FloatFormat(4));
|
|
floatformats.push_back(FloatFormat(8));
|
|
}
|
|
}
|
|
|
|
/// The pcode model for floating point encoding assumes that a
|
|
/// consistent encoding is used for all values of a given size.
|
|
/// This routine fetches the FloatFormat object given the size,
|
|
/// in bytes, of the desired encoding.
|
|
/// \param size is the size of the floating-point value in bytes
|
|
/// \return a pointer to the floating-point format
|
|
const FloatFormat *Translate::getFloatFormat(int4 size) const
|
|
|
|
{
|
|
vector<FloatFormat>::const_iterator iter;
|
|
|
|
for(iter=floatformats.begin();iter!=floatformats.end();++iter) {
|
|
if ((*iter).getSize() == size)
|
|
return &(*iter);
|
|
}
|
|
return (const FloatFormat *)0;
|
|
}
|
|
|
|
/// A convenience method for passing around p-code operations via stream.
|
|
/// A single p-code operation is parsed from an \<op> element and
|
|
/// returned to the application via the PcodeEmit::dump method.
|
|
/// \param addr is the address (of the instruction) to associate with the p-code op
|
|
/// \param decoder is the stream decoder
|
|
void PcodeEmit::decodeOp(const Address &addr,Decoder &decoder)
|
|
|
|
{
|
|
int4 opcode;
|
|
int4 isize;
|
|
VarnodeData outvar;
|
|
VarnodeData invar[16];
|
|
VarnodeData *outptr;
|
|
|
|
uint4 elemId = decoder.openElement(ELEM_OP);
|
|
isize = decoder.readSignedInteger(ATTRIB_SIZE);
|
|
outptr = &outvar;
|
|
if (isize <= 16)
|
|
opcode = PcodeOpRaw::decode(decoder, isize, invar, &outptr);
|
|
else {
|
|
vector<VarnodeData> varStorage(isize,VarnodeData());
|
|
opcode = PcodeOpRaw::decode(decoder, isize, varStorage.data(), &outptr);
|
|
}
|
|
decoder.closeElement(elemId);
|
|
dump(addr,(OpCode)opcode,outptr,invar,isize);
|
|
}
|
|
|
|
} // End namespace ghidra
|