mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
369 lines
12 KiB
C++
369 lines
12 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 "sleighbase.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
const uint4 SleighBase::MAX_UNIQUE_SIZE = 128;
|
|
|
|
int4 SourceFileIndexer::index(const string filename){
|
|
auto it = fileToIndex.find(filename);
|
|
if (fileToIndex.end() != it){
|
|
return it->second;
|
|
}
|
|
fileToIndex[filename] = leastUnusedIndex;
|
|
indexToFile[leastUnusedIndex] = filename;
|
|
return leastUnusedIndex++;
|
|
}
|
|
|
|
int4 SourceFileIndexer::getIndex(string filename){
|
|
return fileToIndex[filename];
|
|
}
|
|
|
|
string SourceFileIndexer::getFilename(int4 index){
|
|
return indexToFile[index];
|
|
}
|
|
|
|
void SourceFileIndexer::decode(Decoder &decoder)
|
|
|
|
{
|
|
uint4 el = decoder.openElement(sla::ELEM_SOURCEFILES);
|
|
while(decoder.peekElement() == sla::ELEM_SOURCEFILE) {
|
|
int4 subel = decoder.openElement();
|
|
string filename = decoder.readString(sla::ATTRIB_NAME);
|
|
int4 index = decoder.readSignedInteger(sla::ATTRIB_INDEX);
|
|
decoder.closeElement(subel);
|
|
fileToIndex[filename] = index;
|
|
indexToFile[index] = filename;
|
|
}
|
|
decoder.closeElement(el);
|
|
}
|
|
|
|
void SourceFileIndexer::encode(Encoder &encoder) const
|
|
|
|
{
|
|
encoder.openElement(sla::ELEM_SOURCEFILES);
|
|
for (int4 i = 0; i < leastUnusedIndex; ++i){
|
|
encoder.openElement(sla::ELEM_SOURCEFILE);
|
|
encoder.writeString(sla::ATTRIB_NAME, indexToFile.at(i));
|
|
encoder.writeSignedInteger(sla::ATTRIB_INDEX, i);
|
|
encoder.closeElement(sla::ELEM_SOURCEFILE);
|
|
}
|
|
encoder.closeElement(sla::ELEM_SOURCEFILES);
|
|
}
|
|
|
|
SleighBase::SleighBase(void)
|
|
|
|
{
|
|
root = (SubtableSymbol *)0;
|
|
maxdelayslotbytes = 0;
|
|
unique_allocatemask = 0;
|
|
numSections = 0;
|
|
}
|
|
|
|
/// Assuming the symbol table is populated, iterate through the table collecting
|
|
/// registers (for the map), user-op names, and context fields.
|
|
void SleighBase::buildXrefs(vector<string> &errorPairs)
|
|
|
|
{
|
|
SymbolScope *glb = symtab.getGlobalScope();
|
|
SymbolTree::const_iterator iter;
|
|
SleighSymbol *sym;
|
|
ostringstream s;
|
|
|
|
for(iter=glb->begin();iter!=glb->end();++iter) {
|
|
sym = *iter;
|
|
if (sym->getType() == SleighSymbol::varnode_symbol) {
|
|
pair<VarnodeData,string> ins(((VarnodeSymbol *)sym)->getFixedVarnode(),sym->getName());
|
|
pair<map<VarnodeData,string>::iterator,bool> res = varnode_xref.insert(ins);
|
|
if (!res.second) {
|
|
errorPairs.push_back(sym->getName());
|
|
errorPairs.push_back((*(res.first)).second);
|
|
}
|
|
}
|
|
else if (sym->getType() == SleighSymbol::userop_symbol) {
|
|
int4 index = ((UserOpSymbol *)sym)->getIndex();
|
|
while(userop.size() <= index)
|
|
userop.push_back("");
|
|
userop[index] = sym->getName();
|
|
}
|
|
else if (sym->getType() == SleighSymbol::context_symbol) {
|
|
ContextSymbol *csym = (ContextSymbol *)sym;
|
|
ContextField *field = (ContextField *)csym->getPatternValue();
|
|
int4 startbit = field->getStartBit();
|
|
int4 endbit = field->getEndBit();
|
|
registerContext(csym->getName(),startbit,endbit);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// If \b this SleighBase is being reused with a new program, the context
|
|
/// variables need to be registered with the new program's database
|
|
void SleighBase::reregisterContext(void)
|
|
|
|
{
|
|
SymbolScope *glb = symtab.getGlobalScope();
|
|
SymbolTree::const_iterator iter;
|
|
SleighSymbol *sym;
|
|
for(iter=glb->begin();iter!=glb->end();++iter) {
|
|
sym = *iter;
|
|
if (sym->getType() == SleighSymbol::context_symbol) {
|
|
ContextSymbol *csym = (ContextSymbol *)sym;
|
|
ContextField *field = (ContextField *)csym->getPatternValue();
|
|
int4 startbit = field->getStartBit();
|
|
int4 endbit = field->getEndBit();
|
|
registerContext(csym->getName(),startbit,endbit);
|
|
}
|
|
}
|
|
}
|
|
|
|
const VarnodeData &SleighBase::getRegister(const string &nm) const
|
|
|
|
{
|
|
VarnodeSymbol *sym = (VarnodeSymbol *)findSymbol(nm);
|
|
if (sym == (VarnodeSymbol *)0)
|
|
throw SleighError("Unknown register name: "+nm);
|
|
if (sym->getType() != SleighSymbol::varnode_symbol)
|
|
throw SleighError("Symbol is not a register: "+nm);
|
|
return sym->getFixedVarnode();
|
|
}
|
|
|
|
string SleighBase::getRegisterName(AddrSpace *base,uintb off,int4 size) const
|
|
|
|
{
|
|
VarnodeData sym;
|
|
sym.space = base;
|
|
sym.offset = off;
|
|
sym.size = size;
|
|
map<VarnodeData,string>::const_iterator iter = varnode_xref.upper_bound(sym); // First point greater than offset
|
|
if (iter == varnode_xref.begin()) return "";
|
|
iter--;
|
|
const VarnodeData &point((*iter).first);
|
|
if (point.space != base) return "";
|
|
uintb offbase = point.offset;
|
|
if (point.offset+point.size >= off+size)
|
|
return (*iter).second;
|
|
|
|
while(iter != varnode_xref.begin()) {
|
|
--iter;
|
|
const VarnodeData &point((*iter).first);
|
|
if ((point.space != base)||(point.offset != offbase)) return "";
|
|
if (point.offset+point.size >= off+size)
|
|
return (*iter).second;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
void SleighBase::getAllRegisters(map<VarnodeData,string> ®list) const
|
|
|
|
{
|
|
reglist = varnode_xref;
|
|
}
|
|
|
|
void SleighBase::getUserOpNames(vector<string> &res) const
|
|
|
|
{
|
|
res = userop; // Return list of all language defined user ops (with index)
|
|
}
|
|
|
|
/// Write a tag fully describing the details of the space.
|
|
/// \param encoder is the stream being written
|
|
/// \param spc is the given address space
|
|
void SleighBase::encodeSlaSpace(Encoder &encoder,AddrSpace *spc) const
|
|
|
|
{
|
|
if (spc->getType() == IPTR_INTERNAL)
|
|
encoder.openElement(sla::ELEM_SPACE_UNIQUE);
|
|
else if (spc->isOtherSpace())
|
|
encoder.openElement(sla::ELEM_SPACE_OTHER);
|
|
else
|
|
encoder.openElement(sla::ELEM_SPACE);
|
|
encoder.writeString(sla::ATTRIB_NAME,spc->getName());
|
|
encoder.writeSignedInteger(sla::ATTRIB_INDEX, spc->getIndex());
|
|
encoder.writeBool(sla::ATTRIB_BIGENDIAN, isBigEndian());
|
|
encoder.writeSignedInteger(sla::ATTRIB_DELAY, spc->getDelay());
|
|
// if (spc->getDelay() != spc->getDeadcodeDelay())
|
|
// encoder.writeSignedInteger(sla::ATTRIB_DEADCODEDELAY, spc->getDeadcodeDelay());
|
|
encoder.writeSignedInteger(sla::ATTRIB_SIZE, spc->getAddrSize());
|
|
if (spc->getWordSize() > 1)
|
|
encoder.writeSignedInteger(sla::ATTRIB_WORDSIZE, spc->getWordSize());
|
|
encoder.writeBool(sla::ATTRIB_PHYSICAL, spc->hasPhysical());
|
|
if (spc->getType() == IPTR_INTERNAL)
|
|
encoder.closeElement(sla::ELEM_SPACE_UNIQUE);
|
|
else if (spc->isOtherSpace())
|
|
encoder.closeElement(sla::ELEM_SPACE_OTHER);
|
|
else
|
|
encoder.closeElement(sla::ELEM_SPACE);
|
|
}
|
|
|
|
/// This does the bulk of the work of creating a .sla file
|
|
/// \param encoder is the stream encoder
|
|
void SleighBase::encode(Encoder &encoder) const
|
|
|
|
{
|
|
encoder.openElement(sla::ELEM_SLEIGH);
|
|
encoder.writeSignedInteger(sla::ATTRIB_VERSION, sla::FORMAT_VERSION);
|
|
encoder.writeBool(sla::ATTRIB_BIGENDIAN, isBigEndian());
|
|
encoder.writeSignedInteger(sla::ATTRIB_ALIGN, alignment);
|
|
encoder.writeUnsignedInteger(sla::ATTRIB_UNIQBASE, getUniqueBase());
|
|
if (maxdelayslotbytes > 0)
|
|
encoder.writeUnsignedInteger(sla::ATTRIB_MAXDELAY, maxdelayslotbytes);
|
|
if (unique_allocatemask != 0)
|
|
encoder.writeUnsignedInteger(sla::ATTRIB_UNIQMASK, unique_allocatemask);
|
|
if (numSections != 0)
|
|
encoder.writeUnsignedInteger(sla::ATTRIB_NUMSECTIONS, numSections);
|
|
indexer.encode(encoder);
|
|
encoder.openElement(sla::ELEM_SPACES);
|
|
encoder.writeString(sla::ATTRIB_DEFAULTSPACE, getDefaultCodeSpace()->getName());
|
|
for(int4 i=0;i<numSpaces();++i) {
|
|
AddrSpace *spc = getSpace(i);
|
|
if (spc == (AddrSpace *)0) continue;
|
|
if ((spc->getType()==IPTR_CONSTANT) ||
|
|
(spc->getType()==IPTR_FSPEC)||
|
|
(spc->getType()==IPTR_IOP)||
|
|
(spc->getType()==IPTR_JOIN))
|
|
continue;
|
|
encodeSlaSpace(encoder,spc);
|
|
}
|
|
encoder.closeElement(sla::ELEM_SPACES);
|
|
symtab.encode(encoder);
|
|
encoder.closeElement(sla::ELEM_SLEIGH);
|
|
}
|
|
|
|
/// This is identical to the functionality of decodeSpace, but the AddrSpace information is stored
|
|
/// in the .sla file format.
|
|
/// \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 *SleighBase::decodeSlaSpace(Decoder &decoder,const Translate *trans)
|
|
|
|
{
|
|
uint4 elemId = decoder.openElement();
|
|
AddrSpace *res;
|
|
int4 index = 0;
|
|
int4 addressSize = 0;
|
|
int4 delay = -1;
|
|
int4 deadcodedelay = -1;
|
|
string name;
|
|
int4 wordsize = 1;
|
|
bool bigEnd = false;
|
|
uint4 flags = 0;
|
|
for (;;) {
|
|
uint4 attribId = decoder.getNextAttributeId();
|
|
if (attribId == 0) break;
|
|
if (attribId == sla::ATTRIB_NAME) {
|
|
name = decoder.readString();
|
|
}
|
|
if (attribId == sla::ATTRIB_INDEX)
|
|
index = decoder.readSignedInteger();
|
|
else if (attribId == sla::ATTRIB_SIZE)
|
|
addressSize = decoder.readSignedInteger();
|
|
else if (attribId == sla::ATTRIB_WORDSIZE)
|
|
wordsize = decoder.readSignedInteger();
|
|
else if (attribId == sla::ATTRIB_BIGENDIAN) {
|
|
bigEnd = decoder.readBool();
|
|
}
|
|
else if (attribId == sla::ATTRIB_DELAY)
|
|
delay = decoder.readSignedInteger();
|
|
else if (attribId == sla::ATTRIB_PHYSICAL) {
|
|
if (decoder.readBool())
|
|
flags |= AddrSpace::hasphysical;
|
|
}
|
|
}
|
|
decoder.closeElement(elemId);
|
|
if (deadcodedelay == -1)
|
|
deadcodedelay = delay; // If deadcodedelay attribute not present, set it to delay
|
|
if (index == 0)
|
|
throw LowlevelError("Expecting index attribute");
|
|
if (elemId == sla::ELEM_SPACE_UNIQUE)
|
|
res = new UniqueSpace(this,trans,index,flags);
|
|
else if (elemId == sla::ELEM_SPACE_OTHER)
|
|
res = new OtherSpace(this,trans,index);
|
|
else {
|
|
if (addressSize == 0 || delay == -1 || name.size() == 0)
|
|
throw LowlevelError("Expecting size/delay/name attributes");
|
|
res = new AddrSpace(this,trans,IPTR_PROCESSOR,name,bigEnd,addressSize,wordsize,index,flags,delay,deadcodedelay);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/// This is identical in functionality to decodeSpaces but the AddrSpace information
|
|
/// is stored in the .sla file format.
|
|
/// \param decoder is the stream decoder
|
|
/// \param trans is the processor translator to be associated with the spaces
|
|
void SleighBase::decodeSlaSpaces(Decoder &decoder,const Translate *trans)
|
|
|
|
{
|
|
// The first space should always be the constant space
|
|
insertSpace(new ConstantSpace(this,trans));
|
|
|
|
uint4 elemId = decoder.openElement(sla::ELEM_SPACES);
|
|
string defname = decoder.readString(sla::ATTRIB_DEFAULTSPACE);
|
|
while(decoder.peekElement() != 0) {
|
|
AddrSpace *spc = decodeSlaSpace(decoder,trans);
|
|
insertSpace(spc);
|
|
}
|
|
decoder.closeElement(elemId);
|
|
AddrSpace *spc = getSpaceByName(defname);
|
|
if (spc == (AddrSpace *)0)
|
|
throw LowlevelError("Bad 'defaultspace' attribute: "+defname);
|
|
setDefaultCodeSpace(spc->getIndex());
|
|
}
|
|
|
|
/// This parses the main \<sleigh> tag (from a .sla file), which includes the description
|
|
/// of address spaces and the symbol table, with its associated decoding tables
|
|
/// \param decoder is the stream to decode
|
|
void SleighBase::decode(Decoder &decoder)
|
|
|
|
{
|
|
maxdelayslotbytes = 0;
|
|
unique_allocatemask = 0;
|
|
numSections = 0;
|
|
int4 version = 0;
|
|
uint4 el = decoder.openElement(sla::ELEM_SLEIGH);
|
|
uint4 attrib = decoder.getNextAttributeId();
|
|
while(attrib != 0) {
|
|
if (attrib == sla::ATTRIB_BIGENDIAN)
|
|
setBigEndian(decoder.readBool());
|
|
else if (attrib == sla::ATTRIB_ALIGN)
|
|
alignment = decoder.readSignedInteger();
|
|
else if (attrib == sla::ATTRIB_UNIQBASE)
|
|
setUniqueBase(decoder.readUnsignedInteger());
|
|
else if (attrib == sla::ATTRIB_MAXDELAY)
|
|
maxdelayslotbytes = decoder.readUnsignedInteger();
|
|
else if (attrib == sla::ATTRIB_UNIQMASK)
|
|
unique_allocatemask = decoder.readUnsignedInteger();
|
|
else if (attrib == sla::ATTRIB_NUMSECTIONS)
|
|
numSections = decoder.readUnsignedInteger();
|
|
else if (attrib == sla::ATTRIB_VERSION)
|
|
version = decoder.readSignedInteger();
|
|
attrib = decoder.getNextAttributeId();
|
|
}
|
|
if (version != sla::FORMAT_VERSION)
|
|
throw LowlevelError(".sla file has wrong format");
|
|
indexer.decode(decoder);
|
|
decodeSlaSpaces(decoder,this);
|
|
symtab.decode(decoder,this);
|
|
decoder.closeElement(el);
|
|
root = (SubtableSymbol *)symtab.getGlobalScope()->findSymbol("instruction");
|
|
vector<string> errorPairs;
|
|
buildXrefs(errorPairs);
|
|
if (!errorPairs.empty())
|
|
throw SleighError("Duplicate register pairs");
|
|
}
|
|
|
|
} // End namespace ghidra
|