Foundation for replacing fold's dead/abstract binary encoder with a real
SSA P-Code frontend. Verified root-cause gap: serializeBinaryIR() emits only
data-discovery nodes (sections/strings/imports/exports); the control-flow
BinaryExprKind values (BasicBlock/CallSite/...) are never constructed, so the
Z3-BB encoder's encodable filter is always empty and encodeBinaryBBs never
runs — hence 0 binary Z3 findings. The P-Code pipeline supersedes that path.
Phase 0 — docs/internal/ghidra-pcode-integration-research.md: P-Code opcode
core, the decompiler C++ embedding API (LoadImage/SleighArchitecture/Funcdata),
the CORE+DECCORE+SLEIGH source sets, no-bison finding, and pypcode as build
reference.
Phase 1 — vendored Ghidra decompiler at pinned commit (Ghidra_12.1_build,
7e89d94e34...) under third_party/ghidra-decompiler/ (cpp + AArch64 specs +
Apache-2.0 LICENSE + VENDOR.md + .gitignore for build artifacts). Validated by
hand on this clang-21/macOS toolchain:
- full SSA engine (CORE+DECCORE+SLEIGH, 80 files incl. heritage/funcdata/
action) compiles clean, -std=c++11 -O2, NO BFD, NO bison (generated
parsers are committed upstream; ruleparse isn't in the lib set);
- sleigh compiler builds; AArch64 .slaspec -> .sla compiles;
- tools/pcode_dump.cpp (custom LoadImage, no BFD) lifts the real arm64
sym._div_by_zero_direct to correct P-Code: `mov w9,#0 -> COPY(const 0)`,
`sdiv -> INT_SDIV(w8,w9)` — divisor zero-ness explicit in the data flow,
so the Phase-3 encoder proves div-by-zero trivially, multi-arch.
Not yet meson-wired (next step) and no fold-side IR/encoder yet (Phase 2-3);
this commit is the validated vendor + smoke tool.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
682 lines
22 KiB
C++
682 lines
22 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 "space.hh"
|
|
#include "translate.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
AttributeId ATTRIB_BASE = AttributeId("base",89);
|
|
AttributeId ATTRIB_DEADCODEDELAY = AttributeId("deadcodedelay",90);
|
|
AttributeId ATTRIB_DELAY = AttributeId("delay", 91);
|
|
AttributeId ATTRIB_LOGICALSIZE = AttributeId("logicalsize",92);
|
|
AttributeId ATTRIB_PHYSICAL = AttributeId("physical",93);
|
|
|
|
// ATTRIB_PIECE is a special attribute for supporting the legacy attributes "piece1", "piece2", ..., "piece9",
|
|
// It is effectively a sequence of indexed attributes for use with Encoder::writeStringIndexed.
|
|
// The index starts at the ids reserved for "piece1" thru "piece9" but can extend farther.
|
|
AttributeId ATTRIB_PIECE = AttributeId("piece",94); // Open slots 94-102
|
|
|
|
/// Calculate \e highest based on \e addressSize, and \e wordsize.
|
|
/// This also calculates the default pointerLowerBound
|
|
void AddrSpace::calcScaleMask(void)
|
|
|
|
{
|
|
highest = calc_mask(addressSize); // Maximum address
|
|
highest = highest * wordsize + (wordsize-1); // Maximum byte address
|
|
pointerLowerBound = 0;
|
|
pointerUpperBound = highest;
|
|
uintb bufferSize = (addressSize < 3) ? 0x100 : 0x1000;
|
|
pointerLowerBound += bufferSize;
|
|
pointerUpperBound -= bufferSize;
|
|
}
|
|
|
|
/// Initialize an address space with its basic attributes
|
|
/// \param m is the space manager associated with the new space
|
|
/// \param t is the processor translator associated with the new space
|
|
/// \param tp is the type of the new space (PROCESSOR, CONSTANT, INTERNAL,...)
|
|
/// \param nm is the name of the new space
|
|
/// \param bigEnd is \b true for big endian encoding
|
|
/// \param size is the (offset encoding) size of the new space
|
|
/// \param ws is the number of bytes in an addressable unit
|
|
/// \param ind is the integer identifier for the new space
|
|
/// \param fl can be 0 or AddrSpace::hasphysical
|
|
/// \param dl is the number of rounds to delay heritage for the new space
|
|
/// \param dead is the number of rounds to delay before dead code removal
|
|
AddrSpace::AddrSpace(AddrSpaceManager *m,const Translate *t,spacetype tp,const string &nm,bool bigEnd,
|
|
uint4 size,uint4 ws, int4 ind,uint4 fl,int4 dl,int4 dead)
|
|
{
|
|
refcount = 0; // No references to this space yet
|
|
manage = m;
|
|
trans = t;
|
|
type = tp;
|
|
name = nm;
|
|
addressSize = size;
|
|
wordsize = ws;
|
|
index = ind;
|
|
delay = dl;
|
|
deadcodedelay = dead;
|
|
minimumPointerSize = 0; // (initially) assume pointers must match the space size exactly
|
|
shortcut = ' '; // Placeholder meaning shortcut is unassigned
|
|
|
|
// These are the flags we allow to be set from constructor
|
|
flags = (fl & hasphysical);
|
|
if (bigEnd)
|
|
flags |= big_endian;
|
|
flags |= (heritaged | does_deadcode); // Always on unless explicitly turned off in derived constructor
|
|
|
|
calcScaleMask();
|
|
}
|
|
|
|
/// This is a partial constructor, for initializing a space
|
|
/// via XML
|
|
/// \param m the associated address space manager
|
|
/// \param t is the processor translator
|
|
/// \param tp the basic type of the space
|
|
AddrSpace::AddrSpace(AddrSpaceManager *m,const Translate *t,spacetype tp)
|
|
|
|
{
|
|
refcount = 0;
|
|
manage = m;
|
|
trans = t;
|
|
type = tp;
|
|
flags = (heritaged | does_deadcode); // Always on unless explicitly turned off in derived constructor
|
|
wordsize = 1;
|
|
minimumPointerSize = 0;
|
|
shortcut = ' ';
|
|
// We let big_endian get set by attribute
|
|
}
|
|
|
|
/// The logical form of the space is truncated from its actual size
|
|
/// Pointers may refer to this original size put the most significant bytes are ignored
|
|
/// \param newsize is the size (in bytes) of the truncated (logical) space
|
|
void AddrSpace::truncateSpace(uint4 newsize)
|
|
|
|
{
|
|
setFlags(truncated);
|
|
addressSize = newsize;
|
|
minimumPointerSize = newsize;
|
|
calcScaleMask();
|
|
}
|
|
|
|
/// \brief Determine if a given point is contained in an address range in \b this address space
|
|
///
|
|
/// The point is specified as an address space and offset pair plus an additional number of bytes to "skip".
|
|
/// A non-negative value is returned if the point falls in the address range.
|
|
/// If the point falls on the first byte of the range, 0 is returned. For the second byte, 1 is returned, etc.
|
|
/// Otherwise -1 is returned.
|
|
/// \param offset is the starting offset of the address range within \b this space
|
|
/// \param size is the size of the address range in bytes
|
|
/// \param pointSpace is the address space of the given point
|
|
/// \param pointOff is the offset of the given point
|
|
/// \param pointSkip is the additional bytes to skip
|
|
/// \return a non-negative value indicating where the point falls in the range, or -1
|
|
int4 AddrSpace::overlapJoin(uintb offset,int4 size,AddrSpace *pointSpace,uintb pointOff,int4 pointSkip) const
|
|
|
|
{
|
|
if (this != pointSpace)
|
|
return -1;
|
|
|
|
uintb dist = wrapOffset(pointOff+pointSkip-offset);
|
|
|
|
if (dist >= size) return -1; // but must fall before op+size
|
|
return (int4) dist;
|
|
}
|
|
|
|
/// Write the main attributes for an address within \b this space.
|
|
/// The caller provides only the \e offset, and this routine fills
|
|
/// in other details pertaining to this particular space.
|
|
/// \param encoder is the stream encoder
|
|
/// \param offset is the offset of the address
|
|
void AddrSpace::encodeAttributes(Encoder &encoder,uintb offset) const
|
|
|
|
{
|
|
encoder.writeSpace(ATTRIB_SPACE,this);
|
|
encoder.writeUnsignedInteger(ATTRIB_OFFSET, offset);
|
|
}
|
|
|
|
/// Write the main attributes of an address with \b this space
|
|
/// and a size. The caller provides the \e offset and \e size,
|
|
/// and other details about this particular space are filled in.
|
|
/// \param encoder is the stream encoder
|
|
/// \param offset is the offset of the address
|
|
/// \param size is the size of the memory location
|
|
void AddrSpace::encodeAttributes(Encoder &encoder,uintb offset,int4 size) const
|
|
|
|
{
|
|
encoder.writeSpace(ATTRIB_SPACE, this);
|
|
encoder.writeUnsignedInteger(ATTRIB_OFFSET, offset);
|
|
encoder.writeSignedInteger(ATTRIB_SIZE, size);
|
|
}
|
|
|
|
/// For an open element describing an address in \b this space, this routine
|
|
/// recovers the offset and possibly the size described by the element
|
|
/// \param decoder is the stream decoder
|
|
/// \param size is a reference where the recovered size should be stored
|
|
/// \return the recovered offset
|
|
uintb AddrSpace::decodeAttributes(Decoder &decoder,uint4 &size) const
|
|
|
|
{
|
|
uintb offset;
|
|
bool foundoffset = false;
|
|
for(;;) {
|
|
uint4 attribId = decoder.getNextAttributeId();
|
|
if (attribId == 0) break;
|
|
if (attribId == ATTRIB_OFFSET) {
|
|
foundoffset = true;
|
|
offset = decoder.readUnsignedInteger();
|
|
}
|
|
else if (attribId == ATTRIB_SIZE) {
|
|
size = decoder.readSignedInteger();
|
|
}
|
|
}
|
|
if (!foundoffset)
|
|
throw LowlevelError("Address is missing offset");
|
|
|
|
return offset;
|
|
}
|
|
|
|
/// Print the \e offset as hexidecimal digits.
|
|
/// \param s is the stream to write to
|
|
/// \param offset is the offset to be printed
|
|
void AddrSpace::printOffset(ostream &s,uintb offset) const
|
|
|
|
{
|
|
s << "0x" << hex << offset;
|
|
}
|
|
|
|
/// This is a printing method for the debugging routines. It
|
|
/// prints taking into account the \e wordsize, adding a
|
|
/// "+n" if the offset is not on-cut with wordsize. It also
|
|
/// returns the expected/typical size of values from this space.
|
|
/// \param s is the stream being written
|
|
/// \param offset is the offset to be printed
|
|
void AddrSpace::printRaw(ostream &s,uintb offset) const
|
|
|
|
{
|
|
int4 sz = getAddrSize();
|
|
if (sz > 4) {
|
|
if ((offset>>32) == 0)
|
|
sz = 4; // Don't print a bunch of zeroes at front of address
|
|
else if ((offset>>48) == 0)
|
|
sz = 6;
|
|
}
|
|
s << "0x" << setfill('0') << setw(2*sz) << hex << byteToAddress(offset,wordsize);
|
|
if (wordsize>1) {
|
|
int4 cut = offset % wordsize;
|
|
if (cut != 0)
|
|
s << '+' << dec << cut;
|
|
}
|
|
}
|
|
|
|
static int4 get_offset_size(const char *ptr,uintb &offset)
|
|
|
|
{ // Get optional size and offset fields from string
|
|
int4 size;
|
|
uint4 val;
|
|
char *ptr2;
|
|
|
|
val = 0; // Defaults
|
|
size = -1;
|
|
if (*ptr == ':') {
|
|
size = strtoul(ptr+1,&ptr2,0);
|
|
if (*ptr2 == '+')
|
|
val = strtoul(ptr2+1,&ptr2,0);
|
|
}
|
|
if (*ptr == '+')
|
|
val = strtoul(ptr+1,&ptr2,0);
|
|
|
|
offset += val; // Adjust offset
|
|
return size;
|
|
}
|
|
|
|
/// For the console mode, an address space can tailor how it
|
|
/// converts user strings into offsets within the space. The
|
|
/// base routine can read and convert register names as well
|
|
/// as absolute hex addresses. A size can be indicated by
|
|
/// appending a ':' and integer, .i.e. 0x1000:2. Offsets within
|
|
/// a register can be indicated by appending a '+' and integer,
|
|
/// i.e. eax+2
|
|
/// \param s is the string to be parsed
|
|
/// \param size is a reference to the size being returned
|
|
/// \return the parsed offset
|
|
uintb AddrSpace::read(const string &s,int4 &size) const
|
|
|
|
{
|
|
const char *enddata;
|
|
char *tmpdata;
|
|
int4 expsize;
|
|
string::size_type append;
|
|
string frontpart;
|
|
uintb offset;
|
|
|
|
append = s.find_first_of(":+");
|
|
try {
|
|
if (append == string::npos) {
|
|
const VarnodeData &point(trans->getRegister(s));
|
|
offset = point.offset;
|
|
size = point.size;
|
|
}
|
|
else {
|
|
frontpart = s.substr(0,append);
|
|
const VarnodeData &point(trans->getRegister(frontpart));
|
|
offset = point.offset;
|
|
size = point.size;
|
|
}
|
|
}
|
|
catch(LowlevelError &err) { // Name doesn't exist
|
|
offset = strtoul(s.c_str(),&tmpdata,0);
|
|
offset = addressToByte(offset,wordsize);
|
|
enddata = (const char *) tmpdata;
|
|
if (enddata - s.c_str() == s.size()) { // If no size or offset override
|
|
size = manage->getDefaultSize(); // Return "natural" size
|
|
return offset;
|
|
}
|
|
size = manage->getDefaultSize();
|
|
}
|
|
if (append != string::npos) {
|
|
enddata = s.c_str()+append;
|
|
expsize = get_offset_size( enddata, offset );
|
|
if (expsize!=-1) {
|
|
size = expsize;
|
|
return offset;
|
|
}
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
/// Walk attributes of the current element and recover all the properties defining
|
|
/// this space. The processor translator, \e trans, and the
|
|
/// \e type must already be filled in.
|
|
/// \param decoder is the stream decoder
|
|
void AddrSpace::decodeBasicAttributes(Decoder &decoder)
|
|
|
|
{
|
|
deadcodedelay = -1;
|
|
for (;;) {
|
|
uint4 attribId = decoder.getNextAttributeId();
|
|
if (attribId == 0) break;
|
|
if (attribId == ATTRIB_NAME) {
|
|
name = decoder.readString();
|
|
}
|
|
if (attribId == ATTRIB_INDEX)
|
|
index = decoder.readSignedInteger();
|
|
else if (attribId == ATTRIB_SIZE)
|
|
addressSize = decoder.readSignedInteger();
|
|
else if (attribId == ATTRIB_WORDSIZE)
|
|
wordsize = decoder.readUnsignedInteger();
|
|
else if (attribId == ATTRIB_BIGENDIAN) {
|
|
if (decoder.readBool())
|
|
flags |= big_endian;
|
|
}
|
|
else if (attribId == ATTRIB_DELAY)
|
|
delay = decoder.readSignedInteger();
|
|
else if (attribId == ATTRIB_DEADCODEDELAY)
|
|
deadcodedelay = decoder.readSignedInteger();
|
|
else if (attribId == ATTRIB_PHYSICAL) {
|
|
if (decoder.readBool())
|
|
flags |= hasphysical;
|
|
}
|
|
|
|
}
|
|
if (deadcodedelay == -1)
|
|
deadcodedelay = delay; // If deadcodedelay attribute not present, set it to delay
|
|
calcScaleMask();
|
|
}
|
|
|
|
void AddrSpace::decode(Decoder &decoder)
|
|
|
|
{
|
|
uint4 elemId = decoder.openElement(); // Multiple tags: <space>, <space_other>, <space_unique>
|
|
decodeBasicAttributes(decoder);
|
|
decoder.closeElement(elemId);
|
|
}
|
|
|
|
const string ConstantSpace::NAME = "const";
|
|
|
|
const int4 ConstantSpace::INDEX = 0;
|
|
|
|
/// This constructs the unique constant space
|
|
/// By convention, the name is always "const" and the index
|
|
/// is always 0.
|
|
/// \param m is the associated address space manager
|
|
/// \param t is the associated processor translator
|
|
ConstantSpace::ConstantSpace(AddrSpaceManager *m,const Translate *t)
|
|
: AddrSpace(m,t,IPTR_CONSTANT,NAME,false,sizeof(uintb),1,INDEX,0,0,0)
|
|
{
|
|
clearFlags(heritaged|does_deadcode|big_endian);
|
|
if (HOST_ENDIAN==1) // Endianness always matches host
|
|
setFlags(big_endian);
|
|
}
|
|
|
|
int4 ConstantSpace::overlapJoin(uintb offset,int4 size,AddrSpace *pointSpace,uintb pointOff,int4 pointSkip) const
|
|
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/// Constants are always printed as hexidecimal values in
|
|
/// the debugger and console dumps
|
|
void ConstantSpace::printRaw(ostream &s,uintb offset) const
|
|
|
|
{
|
|
s << "0x" << hex << offset;
|
|
}
|
|
|
|
/// As the ConstantSpace is never saved, it should never get
|
|
/// decoded either.
|
|
void ConstantSpace::decode(Decoder &decoder)
|
|
|
|
{
|
|
throw LowlevelError("Should never decode the constant space");
|
|
}
|
|
|
|
const string OtherSpace::NAME = "OTHER";
|
|
|
|
const int4 OtherSpace::INDEX = 1;
|
|
|
|
/// Construct the \b other space, which is automatically constructed
|
|
/// by the compiler, and is only constructed once. The name should
|
|
/// always by \b OTHER.
|
|
/// \param m is the associated address space manager
|
|
/// \param t is the associated processor translator
|
|
/// \param ind is the integer identifier
|
|
OtherSpace::OtherSpace(AddrSpaceManager *m,const Translate *t,int4 ind)
|
|
: AddrSpace(m,t,IPTR_PROCESSOR,NAME,false,sizeof(uintb),1,INDEX,0,0,0)
|
|
{
|
|
clearFlags(heritaged|does_deadcode);
|
|
setFlags(is_otherspace);
|
|
}
|
|
|
|
OtherSpace::OtherSpace(AddrSpaceManager *m,const Translate *t)
|
|
: AddrSpace(m,t,IPTR_PROCESSOR)
|
|
{
|
|
clearFlags(heritaged|does_deadcode);
|
|
setFlags(is_otherspace);
|
|
}
|
|
|
|
void OtherSpace::printRaw(ostream &s,uintb offset) const
|
|
|
|
{
|
|
s << "0x" << hex << offset;
|
|
}
|
|
|
|
const string UniqueSpace::NAME = "unique";
|
|
|
|
const uint4 UniqueSpace::SIZE = 4;
|
|
|
|
/// This is the constructor for the \b unique space, which is
|
|
/// automatically constructed by the analysis engine, and
|
|
/// constructed only once. The name should always be \b unique.
|
|
/// \param m is the associated address space manager
|
|
/// \param t is the associated processor translator
|
|
/// \param ind is the integer identifier
|
|
/// \param fl are attribute flags (currently unused)
|
|
UniqueSpace::UniqueSpace(AddrSpaceManager *m,const Translate *t,int4 ind,uint4 fl)
|
|
: AddrSpace(m,t,IPTR_INTERNAL,NAME,t->isBigEndian(),SIZE,1,ind,fl,0,0)
|
|
{
|
|
setFlags(hasphysical);
|
|
}
|
|
|
|
UniqueSpace::UniqueSpace(AddrSpaceManager *m,const Translate *t)
|
|
: AddrSpace(m,t,IPTR_INTERNAL)
|
|
{
|
|
setFlags(hasphysical);
|
|
}
|
|
|
|
const string JoinSpace::NAME = "join";
|
|
|
|
/// This is the constructor for the \b join space, which is automatically constructed by the
|
|
/// analysis engine, and constructed only once. The name should always be \b join.
|
|
/// \param m is the associated address space manager
|
|
/// \param t is the associated processor translator
|
|
/// \param ind is the integer identifier
|
|
JoinSpace::JoinSpace(AddrSpaceManager *m,const Translate *t,int4 ind)
|
|
: AddrSpace(m,t,IPTR_JOIN,NAME,t->isBigEndian(),sizeof(uintm),1,ind,0,0,0)
|
|
{
|
|
// This is a virtual space
|
|
// setFlags(hasphysical);
|
|
clearFlags(heritaged); // This space is never heritaged, but does dead-code analysis
|
|
}
|
|
|
|
int4 JoinSpace::overlapJoin(uintb offset,int4 size,AddrSpace *pointSpace,uintb pointOffset,int4 pointSkip) const
|
|
|
|
{
|
|
if (this == pointSpace) {
|
|
// If the point is in the join space, translate the point into the piece address space
|
|
JoinRecord *pieceRecord = getManager()->findJoin(pointOffset);
|
|
int4 pos;
|
|
Address addr = pieceRecord->getEquivalentAddress(pointOffset + pointSkip, pos);
|
|
pointSpace = addr.getSpace();
|
|
pointOffset = addr.getOffset();
|
|
}
|
|
else {
|
|
if (pointSpace->getType() == IPTR_CONSTANT)
|
|
return -1;
|
|
pointOffset = pointSpace->wrapOffset(pointOffset + pointSkip);
|
|
}
|
|
JoinRecord *joinRecord = getManager()->findJoin(offset);
|
|
// Set up so we traverse pieces in data order
|
|
int4 startPiece,endPiece,dir;
|
|
if (isBigEndian()) {
|
|
startPiece = 0;
|
|
endPiece = joinRecord->numPieces();
|
|
dir = 1;
|
|
}
|
|
else {
|
|
startPiece = joinRecord->numPieces() - 1;
|
|
endPiece = -1;
|
|
dir = -1;
|
|
}
|
|
int4 bytesAccum = 0;
|
|
for(int4 i=startPiece;i!=endPiece;i += dir) {
|
|
const VarnodeData &vData(joinRecord->getPiece(i));
|
|
if (vData.space == pointSpace && pointOffset >= vData.offset && pointOffset <= vData.offset + (vData.size-1)) {
|
|
int4 res = (int4)(pointOffset - vData.offset) + bytesAccum;
|
|
if (res >= size)
|
|
return -1;
|
|
return res;
|
|
}
|
|
bytesAccum += vData.size;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// Encode a \e join address to the stream. This method in the interface only
|
|
/// outputs attributes for a single element, so we are forced to encode what should probably
|
|
/// be recursive elements into an attribute.
|
|
/// \param encoder is the stream encoder
|
|
/// \param offset is the offset within the address space to encode
|
|
void JoinSpace::encodeAttributes(Encoder &encoder,uintb offset) const
|
|
|
|
{
|
|
JoinRecord *rec = getManager()->findJoin(offset); // Record must already exist
|
|
encoder.writeSpace(ATTRIB_SPACE, this);
|
|
int4 num = rec->numPieces();
|
|
if (num > MAX_PIECES)
|
|
throw LowlevelError("Exceeded maximum pieces in one join address");
|
|
for(int4 i=0;i<num;++i) {
|
|
const VarnodeData &vdata( rec->getPiece(i) );
|
|
ostringstream t;
|
|
t << vdata.space->getName() << ":0x";
|
|
t << hex << vdata.offset << ':' << dec << vdata.size;
|
|
encoder.writeStringIndexed(ATTRIB_PIECE, i, t.str());
|
|
}
|
|
if (num == 1)
|
|
encoder.writeUnsignedInteger(ATTRIB_LOGICALSIZE, rec->getUnified().size);
|
|
}
|
|
|
|
/// Encode a \e join address to the stream. This method in the interface only
|
|
/// outputs attributes for a single element, so we are forced to encode what should probably
|
|
/// be recursive elements into an attribute.
|
|
/// \param encoder is the stream encoder
|
|
/// \param offset is the offset within the address space to encode
|
|
/// \param size is the size of the memory location being encoded
|
|
void JoinSpace::encodeAttributes(Encoder &encoder,uintb offset,int4 size) const
|
|
|
|
{
|
|
encodeAttributes(encoder,offset); // Ignore size
|
|
}
|
|
|
|
/// Parse the current element as a join address. Pieces of the join are encoded as a sequence
|
|
/// of ATTRIB_PIECE attributes. "piece1" corresponds to the most significant piece. The
|
|
/// Translate::findAddJoin method is used to construct a logical address within the join space.
|
|
/// \param decoder is the stream decoder
|
|
/// \param size is a reference to be filled in as the size encoded by the tag
|
|
/// \return the offset of the final address encoded by the tag
|
|
uintb JoinSpace::decodeAttributes(Decoder &decoder,uint4 &size) const
|
|
|
|
{
|
|
vector<VarnodeData> pieces;
|
|
uint4 sizesum = 0;
|
|
uint4 logicalsize = 0;
|
|
for(;;) {
|
|
uint4 attribId = decoder.getNextAttributeId();
|
|
if (attribId == 0) break;
|
|
if (attribId == ATTRIB_LOGICALSIZE) {
|
|
logicalsize = decoder.readUnsignedInteger();
|
|
continue;
|
|
}
|
|
else if (attribId == ATTRIB_UNKNOWN)
|
|
attribId = decoder.getIndexedAttributeId(ATTRIB_PIECE);
|
|
if (attribId < ATTRIB_PIECE.getId())
|
|
continue;
|
|
int4 pos = (int4)(attribId - ATTRIB_PIECE.getId());
|
|
if (pos > MAX_PIECES)
|
|
continue;
|
|
while(pieces.size() <= pos)
|
|
pieces.emplace_back();
|
|
VarnodeData &vdat( pieces[pos] );
|
|
|
|
string attrVal = decoder.readString();
|
|
string::size_type offpos = attrVal.find(':');
|
|
if (offpos == string::npos) {
|
|
const Translate *tr = getTrans();
|
|
const VarnodeData &point(tr->getRegister(attrVal));
|
|
vdat = point;
|
|
}
|
|
else {
|
|
string::size_type szpos = attrVal.find(':',offpos+1);
|
|
if (szpos==string::npos)
|
|
throw LowlevelError("join address piece attribute is malformed");
|
|
string spcname = attrVal.substr(0,offpos);
|
|
vdat.space = getManager()->getSpaceByName(spcname);
|
|
istringstream s1(attrVal.substr(offpos+1,szpos));
|
|
s1.unsetf(ios::dec | ios::hex | ios::oct);
|
|
s1 >> vdat.offset;
|
|
istringstream s2(attrVal.substr(szpos+1));
|
|
s2.unsetf(ios::dec | ios::hex | ios::oct);
|
|
s2 >> vdat.size;
|
|
}
|
|
sizesum += vdat.size;
|
|
}
|
|
JoinRecord *rec = getManager()->findAddJoin(pieces,logicalsize);
|
|
size = rec->getUnified().size;
|
|
return rec->getUnified().offset;
|
|
}
|
|
|
|
void JoinSpace::printRaw(ostream &s,uintb offset) const
|
|
|
|
{
|
|
JoinRecord *rec = getManager()->findJoin(offset);
|
|
int4 szsum = 0;
|
|
int4 num = rec->numPieces();
|
|
s << '{';
|
|
for(int4 i=0;i<num;++i) {
|
|
const VarnodeData &vdat( rec->getPiece(i) );
|
|
szsum += vdat.size;
|
|
if (i!=0)
|
|
s << ',';
|
|
vdat.space->printRaw(s,vdat.offset);
|
|
}
|
|
if (num == 1) {
|
|
szsum = rec->getUnified().size;
|
|
s << ':' << szsum;
|
|
}
|
|
s << '}';
|
|
}
|
|
|
|
uintb JoinSpace::read(const string &s,int4 &size) const
|
|
|
|
{
|
|
vector<VarnodeData> pieces;
|
|
int4 szsum = 0;
|
|
int4 i=0;
|
|
while(i < s.size()) {
|
|
pieces.emplace_back(); // Prepare to read next VarnodeData
|
|
string token;
|
|
while((i<s.size())&&(s[i]!=',')) {
|
|
token += s[i];
|
|
i += 1;
|
|
}
|
|
i += 1; // Skip the comma
|
|
try {
|
|
pieces.back() = getTrans()->getRegister(token);
|
|
}
|
|
catch(LowlevelError &err) { // Name doesn't exist
|
|
char tryShortcut = token[0];
|
|
AddrSpace *spc = getManager()->getSpaceByShortcut(tryShortcut);
|
|
if (spc == (AddrSpace *)0)
|
|
throw LowlevelError("Could not parse join string");
|
|
|
|
int4 subsize;
|
|
pieces.back().space = spc;
|
|
pieces.back().offset = spc->read(token.substr(1),subsize);
|
|
pieces.back().size = subsize;
|
|
}
|
|
szsum += pieces.back().size;
|
|
}
|
|
JoinRecord *rec = getManager()->findAddJoin(pieces,0);
|
|
size = szsum;
|
|
return rec->getUnified().offset;
|
|
}
|
|
|
|
void JoinSpace::decode(Decoder &decoder)
|
|
|
|
{
|
|
throw LowlevelError("Should never decode join space");
|
|
}
|
|
|
|
/// \param m is the address space manager
|
|
/// \param t is the processor translator
|
|
OverlaySpace::OverlaySpace(AddrSpaceManager *m,const Translate *t)
|
|
: AddrSpace(m,t,IPTR_PROCESSOR)
|
|
{
|
|
baseSpace = (AddrSpace *)0;
|
|
setFlags(overlay);
|
|
}
|
|
|
|
void OverlaySpace::decode(Decoder &decoder)
|
|
|
|
{
|
|
uint4 elemId = decoder.openElement(ELEM_SPACE_OVERLAY);
|
|
name = decoder.readString(ATTRIB_NAME);
|
|
index = decoder.readSignedInteger(ATTRIB_INDEX);
|
|
|
|
baseSpace = decoder.readSpace(ATTRIB_BASE);
|
|
decoder.closeElement(elemId);
|
|
addressSize = baseSpace->getAddrSize();
|
|
wordsize = baseSpace->getWordSize();
|
|
delay = baseSpace->getDelay();
|
|
deadcodedelay = baseSpace->getDeadcodeDelay();
|
|
calcScaleMask();
|
|
|
|
if (baseSpace->isBigEndian())
|
|
setFlags(big_endian);
|
|
if (baseSpace->hasPhysical())
|
|
setFlags(hasphysical);
|
|
}
|
|
|
|
} // End namespace ghidra
|