Converted StringManager to use vectors

This commit is contained in:
caheckman 2020-04-20 17:31:02 -04:00
parent 0eb48e441f
commit 1529e635fc
11 changed files with 161 additions and 111 deletions

View file

@ -19,6 +19,7 @@
#include "ghidra_translate.hh"
#include "typegrp_ghidra.hh"
#include "comment_ghidra.hh"
#include "string_ghidra.hh"
#include "cpool_ghidra.hh"
#include "inject_ghidra.hh"
@ -346,6 +347,12 @@ void ArchitectureGhidra::buildCommentDB(DocumentStorage &store)
commentdb = new CommentDatabaseGhidra(this);
}
void ArchitectureGhidra::buildStringManager(DocumentStorage &store)
{
stringManager = new GhidraStringManager(this,2048);
}
void ArchitectureGhidra::buildConstantPool(DocumentStorage &store)
{
@ -615,7 +622,7 @@ void ArchitectureGhidra::getBytes(uint1 *buf,int4 size,const Address &inaddr)
readResponseEnd(sin);
}
uint4 ArchitectureGhidra::getStringData(uint1 *buf,const Address &addr,Datatype *ct,int4 maxBytes)
void ArchitectureGhidra::getStringData(vector<uint1> &buffer,const Address &addr,Datatype *ct,int4 maxBytes)
{
sout.write("\000\000\001\004",4);
@ -633,32 +640,28 @@ uint4 ArchitectureGhidra::getStringData(uint1 *buf,const Address &addr,Datatype
readToResponse(sin);
int4 type = readToAnyBurst(sin);
uint4 size = 0;
if (type == 12) {
int4 c = sin.get();
size ^= (c-0x20);
uint4 size = (c-0x20);
c = sin.get();
size ^= ((c-0x20)<<6);
buffer.reserve(size);
uint1 *dblbuf = new uint1[size * 2];
sin.read((char *)dblbuf,size*2);
for (int4 i=0; i < size; i++) {
buf[i] = ((dblbuf[i*2]-'A') << 4) | (dblbuf[i*2 + 1]-'A');
buffer.push_back(((dblbuf[i*2]-'A') << 4) | (dblbuf[i*2 + 1]-'A'));
}
delete [] dblbuf;
}
else if ((type&1)==1) {
ostringstream errmsg;
errmsg << "GHIDRA has no string in the loadimage at " << addr.getShortcut();
addr.printRaw(errmsg);
throw DataUnavailError(errmsg.str());
}
else
throw JavaError("alignment","Expecting bytes or end of query response");
type = readToAnyBurst(sin);
if (type != 13)
throw JavaError("alignment","Expecting byte alignment end");
readResponseEnd(sin);
return size;
type = readToAnyBurst(sin);
}
if ((type&1)==1) {
// Leave the buffer empty
}
else
throw JavaError("alignment","Expecting end of query response");
}
/// \brief Retrieve p-code to inject for a specific context

View file

@ -74,6 +74,7 @@ class ArchitectureGhidra : public Architecture {
virtual PcodeInjectLibrary *buildPcodeInjectLibrary(void);
virtual void buildTypegrp(DocumentStorage &store);
virtual void buildCommentDB(DocumentStorage &store);
virtual void buildStringManager(DocumentStorage &store);
virtual void buildConstantPool(DocumentStorage &store);
virtual void buildContext(DocumentStorage &store);
virtual void buildSpecFile(DocumentStorage &store);
@ -124,7 +125,7 @@ public:
bool getSendParamMeasures(void) const { return sendParamMeasures; } ///< Get the current setting for emitting parameter info
virtual uint4 getStringData(uint1 *buf,const Address &addr,Datatype *ct,int4 maxBytes);
virtual void getStringData(vector<uint1> &buffer,const Address &addr,Datatype *ct,int4 maxBytes);
virtual void printMessage(const string &message) const;
static void segvHandler(int4 sig); ///< Handler for a segment violation (SIGSEGV) signal

View file

@ -127,6 +127,7 @@ void IfaceDecompCapability::registerCommands(IfaceStatus *status)
status->registerCom(new IfcCallFixup(),"fixup","call");
status->registerCom(new IfcCallOtherFixup(),"fixup","callother");
status->registerCom(new IfcVolatile(),"volatile");
status->registerCom(new IfcReadonly(),"readonly");
status->registerCom(new IfcPreferSplit(),"prefersplit");
status->registerCom(new IfcStructureBlocks(),"structure","blocks");
status->registerCom(new IfcAnalyzeRange(), "analyze","range");
@ -2304,6 +2305,22 @@ void IfcVolatile::execute(istream &s)
*status->optr << "Successfully marked range as volatile" << endl;
}
void IfcReadonly::execute(istream &s)
{
int4 size = 0;
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read required address
if (size == 0)
throw IfaceExecutionError("Must specify a size");
Range range( addr.getSpace(), addr.getOffset(), addr.getOffset() + (size-1));
dcp->conf->symboltab->setPropertyRange(Varnode::readonly,range);
*status->optr << "Successfully marked range as readonly" << endl;
}
void IfcPreferSplit::execute(istream &s)
{ // Mark a particular storage location as something we would prefer to split

View file

@ -531,6 +531,11 @@ public:
virtual void execute(istream &s);
};
class IfcReadonly : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
class IfcPreferSplit : public IfaceDecompCommand {
public:
virtual void execute(istream &s);

View file

@ -1216,20 +1216,23 @@ bool PrintC::doEmitWideCharPrefix(void) const
bool PrintC::printCharacterConstant(ostream &s,const Address &addr,Datatype *charType) const
{
const uint1 *buffer;
StringManager *manager = glb->stringManager;
try {
buffer = manager->getStringData(addr, charType);
} catch(DataUnavailError &err) {
// Retrieve UTF8 version of string
const vector<uint1> &buffer(manager->getStringData(addr, charType));
if (buffer.empty())
return false;
}
if (doEmitWideCharPrefix() && charType->getSize() > 1)
s << 'L'; // Print symbol indicating wide character
s << '"';
if (!escapeCharacterData(s,buffer,manager->getMaximumBytes(),charType->getSize(),glb->translate->isBigEndian()))
if (!escapeCharacterData(s,buffer.data(),buffer.size(),1,glb->translate->isBigEndian()))
s << "...\" /* TRUNCATED STRING LITERAL */";
else {
if (buffer.size() > manager->getMaximumBytes())
s << "...\" /* TRUNCATED STRING LITERAL */";
else
s << '"';
}
return true;
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ghidra_string.hh"
#include "string_ghidra.hh"
GhidraStringManager::GhidraStringManager(ArchitectureGhidra *g,int4 max)
: StringManager(max)
@ -28,14 +28,15 @@ GhidraStringManager::~GhidraStringManager(void)
delete [] testBuffer;
}
const uint1 *GhidraStringManager::getStringData(const Address &addr,Datatype *charType)
const vector<uint1> &GhidraStringManager::getStringData(const Address &addr,Datatype *charType)
{
map<Address,const uint1 *>::iterator iter;
map<Address,vector<uint1> >::iterator iter;
iter = stringMap.find(addr);
if (iter != stringMap.end())
return (*iter).second;
int4 size = glb->getStringData(testBuffer, addr, charType, maximumBytes);
return mapBuffer(addr, testBuffer, size);
vector<uint1> &buffer(stringMap[addr]);
glb->getStringData(buffer, addr, charType, maximumBytes);
return buffer;
}

View file

@ -33,7 +33,7 @@ class GhidraStringManager : public StringManager {
public:
GhidraStringManager(ArchitectureGhidra *g,int4 max); ///< Constructor
virtual ~GhidraStringManager(void);
virtual const uint1 *getStringData(const Address &addr,Datatype *charType);
virtual const vector<uint1> &getStringData(const Address &addr,Datatype *charType);
};
#endif

View file

@ -16,21 +16,6 @@
#include "stringmanage.hh"
#include "architecture.hh"
/// Before calling, we must check that there is no other buffer stored at the address.
/// \param addr is the Address to store the buffer at
/// \param buf is the buffer to be copied into storage
/// \param size is the number of bytes in the buffer
/// \return the new permanent copy of the buffer
const uint1 *StringManager::mapBuffer(const Address &addr,const uint1 *buf,int4 size)
{
uint1 *storeBuf = new uint1[size + 1];
stringMap[addr] = storeBuf;
memcpy(storeBuf,buf,size);
storeBuf[size] = 0;
return storeBuf;
}
/// \param max is the maximum number of bytes to allow in a decoded string
StringManager::StringManager(int4 max)
@ -44,16 +29,6 @@ StringManager::~StringManager(void)
clear();
}
void StringManager::clear(void)
{
map<Address,const uint1 *>::iterator iter;
for(iter=stringMap.begin();iter!=stringMap.end();++iter) {
delete [] (*iter).second;
}
}
/// Encode the given unicode codepoint as UTF8 (1, 2, 3, or 4 bytes) and
/// write the bytes to the stream.
/// \param s is the output stream
@ -103,14 +78,8 @@ void StringManager::writeUtf8(ostream &s,int4 codepoint)
bool StringManager::isString(const Address &addr,Datatype *charType)
{
const uint1 *buffer = (const uint1 *)0;
try {
buffer = getStringData(addr,charType);
}
catch(DataUnavailError &err) {
return false;
}
return (buffer != (const uint1 *)0);
const vector<uint1> &buffer(getStringData(addr,charType));
return !buffer.empty();
}
/// Write \<stringmanage> tag, with \<string> sub-tags.
@ -120,15 +89,14 @@ void StringManager::saveXml(ostream &s) const
{
s << "<stringmanage>\n";
map<Address,const uint1 *>::const_iterator iter1;
map<Address,vector<uint1> >::const_iterator iter1;
for(iter1=stringMap.begin();iter1!=stringMap.end();++iter1) {
s << "<string>\n";
(*iter1).first.saveXml(s);
const uint1 *buf = (*iter1).second;
const vector<uint1> &vec( (*iter1).second );
s << " <bytes>\n" << setfill('0');
for(int4 i=0;;++i) {
if (buf[i] == 0) break;
s << hex << setw(2) << (int4)buf[i];
for(int4 i=0;vec.size();++i) {
s << hex << setw(2) << (int4)vec[i];
if (i%20 == 19)
s << "\n ";
}
@ -148,7 +116,7 @@ void StringManager::restoreXml(const Element *el,const AddrSpaceManager *m)
iter = list.begin();
Address addr = Address::restoreXml(*iter, m);
++iter;
vector<uint1> vec;
vector<uint1> &vec(stringMap[addr]);
istringstream is((*iter)->getContent());
int4 val;
char c1, c2;
@ -174,7 +142,6 @@ void StringManager::restoreXml(const Element *el,const AddrSpaceManager *m)
c1 = is.get();
c2 = is.get();
}
mapBuffer(addr,vec.data(),vec.size());
}
/// \param buffer is the byte buffer
@ -300,49 +267,61 @@ StringManagerUnicode::~StringManagerUnicode(void)
delete [] testBuffer;
}
const uint1 *StringManagerUnicode::getStringData(const Address &addr,Datatype *charType)
const vector<uint1> &StringManagerUnicode::getStringData(const Address &addr,Datatype *charType)
{
map<Address,const uint1 *>::iterator iter;
map<Address,vector<uint1> >::iterator iter;
iter = stringMap.find(addr);
if (iter != stringMap.end())
return (*iter).second;
vector<uint1> &vec(stringMap[addr]); // Allocate (initially empty) byte vector
int4 curBufferSize = 0;
int4 charsize = charType->getSize();
bool foundTerminator = false;
try {
do {
int4 amount = 32; // Grab 32 bytes of image at a time
uint4 newBufferSize = curBufferSize + amount;
if (newBufferSize > maximumBytes) {
newBufferSize = maximumBytes;
amount = newBufferSize - curBufferSize;
if (amount == 0) break;
if (amount == 0)
break;
}
glb->loader->loadFill(testBuffer+curBufferSize,amount,addr + curBufferSize);
foundTerminator = hasCharTerminator(testBuffer+curBufferSize,amount,charsize);
glb->loader->loadFill(testBuffer + curBufferSize, amount,
addr + curBufferSize);
foundTerminator = hasCharTerminator(testBuffer + curBufferSize, amount,
charsize);
curBufferSize = newBufferSize;
} while (!foundTerminator);
} catch (DataUnavailError &err) {
return vec; // Return the empty buffer
}
const uint1 *resBuffer;
if (charsize == 1) {
if (!isCharacterConstant(testBuffer,curBufferSize,charsize))
return (const uint1 *)0;
resBuffer = mapBuffer(addr,testBuffer,curBufferSize);
return vec; // Return the empty buffer
vec.reserve(curBufferSize);
vec.assign(testBuffer,testBuffer+curBufferSize);
}
else {
// We need to translate to UTF8
ostringstream s;
if (!writeUnicode(s, testBuffer, curBufferSize, charsize))
return (const uint1 *)0;
return vec; // Return the empty buffer
string resString = s.str();
int4 newSize = resString.size();
if (newSize > maximumBytes)
newSize = maximumBytes;
resBuffer = mapBuffer(addr,(const uint1 *)resString.c_str(),newSize);
vector<uint1> &vec(stringMap[addr]);
vec.reserve(newSize);
const uint1 *ptr = (const uint1 *)resString.c_str();
vec.assign(ptr,ptr+newSize);
}
return resBuffer;
return vec;
}
/// If the string is encoded in UTF8 or ASCII, we get (on average) a bit of check

View file

@ -30,27 +30,25 @@ class Architecture;
/// Stores the decoded string until its needed for presentation.
class StringManager {
protected:
map<Address,const uint1 *> stringMap; ///< Map from address to string (in UTF8 format)
map<Address,vector<uint1> > stringMap; ///< Map from address to string (in UTF8 format)
int4 maximumBytes; ///< Maximum bytes (in UTF8 encoding) allowed
const uint1 *mapBuffer(const Address &addr,const uint1 *buf,int4 size); ///< Move a decoded buffer into storage
public:
StringManager(int4 max); ///< Constructor
virtual ~StringManager(void); ///< Destructor
int4 getMaximumBytes(void) const { return maximumBytes; } ///< Return the maximum bytes allowed in a string decoding
void clear(void); ///< Clear out any cached strings
void clear(void) { stringMap.clear(); } ///< Clear out any cached strings
bool isString(const Address &addr,Datatype *charType); // Determine if data at the given address is a string
/// \brief Retrieve string data at the given address as a UTF8 byte array
///
/// If the address does not represent string data, null is returned. Otherwise,
/// If the address does not represent string data, a zero length vector is returned. Otherwise,
/// the string data is fetched, converted to a UTF8 encoding, cached and returned.
/// \param addr is the given address
/// \param charType is a character data-type indicating the encoding
/// \return the byte array of UTF8 data (or null)
virtual const uint1 *getStringData(const Address &addr,Datatype *charType)=0;
/// \return the byte array of UTF8 data
virtual const vector<uint1> &getStringData(const Address &addr,Datatype *charType)=0;
void saveXml(ostream &s) const; ///< Save cached strings to a stream as XML
void restoreXml(const Element *el,const AddrSpaceManager *m); ///< Restore string cache from XML
@ -72,7 +70,7 @@ public:
StringManagerUnicode(Architecture *g,int4 max); ///< Constructor
virtual ~StringManagerUnicode(void);
virtual const uint1 *getStringData(const Address &addr,Datatype *charType);
virtual const vector<uint1> &getStringData(const Address &addr,Datatype *charType);
bool isCharacterConstant(const uint1 *buf,int4 size,int4 charsize) const; ///< Return \b true if buffer looks like unicode
bool writeUnicode(ostream &s,uint1 *buffer,int4 size,int4 charsize); ///< Write unicode byte array to stream (as UTF8)
};

View file

@ -1181,6 +1181,35 @@ public class DecompileCallback {
return listing.getFunctionAt(addr);
}
/**
* Return true if there are no "replacement" characters in the string
* @param string is the string to test
* @return true if no replacements
*/
private boolean isValidChars(String string) {
char replaceChar = '\ufffd';
for (int i = 0; i < string.length(); ++i) {
char c = string.charAt(i);
if (c == replaceChar) {
return false;
}
}
return true;
}
/**
* Check for a string at an address and return a UTF8 encoded byte array.
* If there is already data present at the address, use this to determine the
* string encoding. Otherwise use the data-type info passed in to determine the encoding.
* Check that the bytes at the address represent a valid string encoding that doesn't
* exceed the maximum byte limit passed in. Return null if the string is invalid.
* Return the string translated into a UTF8 byte array otherwise. A (valid) empty
* string is returned as a zero length array.
* @param addrString is the XML encoded address and maximum byte limit
* @param dtName is the name of a character data-type
* @param dtId is the id associated with the character data-type
* @return the UTF8 encoded byte array or null
*/
public byte[] getStringData(String addrString, String dtName, String dtId) {
Address addr;
int maxBytes;
@ -1199,9 +1228,21 @@ public class DecompileCallback {
Settings settings = SettingsImpl.NO_SETTINGS;
AbstractStringDataType dataType = null;
if (data != null) {
settings = data;
if (data.getDataType() instanceof AbstractStringDataType) {
settings = data;
dataType = (AbstractStringDataType) data.getDataType();
int len = data.getLength();
if (len > 0) {
long diff = addr.subtract(data.getAddress()) *
addr.getAddressSpace().getAddressableUnitSize();
if (diff < 0 || diff >= len) {
return null;
}
len -= diff;
if (len < maxBytes) {
maxBytes = len;
}
}
}
}
if (dataType == null) {
@ -1228,17 +1269,17 @@ public class DecompileCallback {
}
}
MemoryBufferImpl buf = new MemoryBufferImpl(program.getMemory(), addr, 64);
Object value = dataType.getValue(buf, settings, maxBytes);
if (!(value instanceof String)) {
StringDataInstance stringInstance = dataType.getStringDataInstance(buf, settings, maxBytes);
int len = stringInstance.getStringLength();
if (len < 0 || len > maxBytes) {
return null;
}
String stringVal = (String) value;
byte[] res = stringVal.getBytes(utf8Charset);
if (res.length > maxBytes) {
byte[] trim = new byte[maxBytes];
System.arraycopy(res, 0, trim, 0, maxBytes);
String stringVal = stringInstance.getStringValue();
if (!isValidChars(stringVal)) {
return null;
}
return res;
return stringVal.getBytes(utf8Charset);
}
//==================================================================================================

View file

@ -789,19 +789,21 @@ public class DecompileProcess {
String dtId = readQueryString();
byte[] res = callback.getStringData(addr, dtName, dtId);
write(query_response_start);
if ((res != null) && (res.length > 0)) {
int sz = res.length;
if (res != null) {
int sz = res.length + 1; // We add a null terminator character
int sz1 = (sz & 0x3f) + 0x20;
sz >>>= 6;
int sz2 = (sz & 0x3f) + 0x20;
write(byte_start);
write(sz1);
write(sz2);
byte[] dblres = new byte[res.length * 2];
byte[] dblres = new byte[res.length * 2 + 2];
for (int i = 0; i < res.length; i++) {
dblres[i * 2] = (byte) (((res[i] >> 4) & 0xf) + 65);
dblres[i * 2 + 1] = (byte) ((res[i] & 0xf) + 65);
}
dblres[res.length * 2] = 65; // Adding null terminator
dblres[res.length * 2 + 1] = 65;
write(dblres);
write(byte_end);
}