Files
renderdoc/renderdoc/android/jdwp_util.cpp
T
2025-03-14 18:54:37 +00:00

309 lines
7.3 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2025 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "jdwp.h"
// The handshake and packet spec is defined in:
// https://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp-spec.html
// This gives the overall structure of each packet, plus the format of the 'basic' types like
// objectID, value, location, etc.
namespace JDWP
{
uint32_t Command::idalloc = 42;
uint32_t Command::Send(StreamWriter &writer)
{
id = idalloc++;
length = 11 + (uint32_t)data.size();
uint32_t tmp = EndianSwap(length);
writer.Write(tmp);
tmp = EndianSwap(id);
writer.Write(tmp);
// no need to endian swap these
byte flags = 0;
writer.Write(flags);
writer.Write(commandset);
writer.Write(command);
// already endian swapped
writer.Write(data.data(), data.size());
writer.Flush();
return id;
}
void Command::Recv(StreamReader &reader)
{
reader.Read(length);
length = EndianSwap(length);
reader.Read(id);
id = EndianSwap(id);
byte flags = 0;
reader.Read(flags);
if(flags == 0x80)
{
commandset = CommandSet::Unknown;
command = 0;
uint16_t errInt = 0;
reader.Read(errInt);
error = (Error)EndianSwap(errInt);
}
else
{
reader.Read(commandset);
reader.Read(command);
error = Error::None;
}
data.clear();
if(length >= 11)
{
data.resize(length - 11);
reader.Read(&data[0], data.size());
}
}
CommandData Command::GetData()
{
return CommandData(data);
}
void CommandData::ReadBytes(void *bytes, size_t length)
{
// read the data if there's enough, otherwise set to 0s.
if(offs + length <= data.size())
memcpy(bytes, data.data() + offs, length);
else
memset(bytes, 0, length);
// always increment the offset so we can see how much we over-read in Done().
offs += length;
}
void CommandData::WriteBytes(const void *bytes, size_t length)
{
data.append((const byte *)bytes, length);
}
template <>
CommandData &CommandData::Read(rdcstr &str)
{
uint32_t length = 0;
Read(length);
str.resize(length);
ReadBytes(&str[0], length);
return *this;
}
template <>
CommandData &CommandData::Write(const rdcstr &str)
{
uint32_t length = (uint32_t)str.size();
Write(length);
WriteBytes(str.c_str(), str.size());
return *this;
}
template <>
CommandData &CommandData::Read(taggedObjectID &id)
{
Read((byte &)id.tag);
Read(id.id);
return *this;
}
template <>
CommandData &CommandData::Write(const taggedObjectID &id)
{
Write((const byte &)id.tag);
Write(id.id);
return *this;
}
template <>
CommandData &CommandData::Read(value &val)
{
Read((byte &)val.tag);
switch(val.tag)
{
case Tag::Unknown: break;
case Tag::Array: Read(val.Array); break;
case Tag::Byte: Read(val.Byte); break;
case Tag::Char: Read(val.Char); break;
case Tag::Object: Read(val.Object); break;
case Tag::Float: Read(val.Float); break;
case Tag::Double: Read(val.Double); break;
case Tag::Int: Read(val.Int); break;
case Tag::Long: Read(val.Long); break;
case Tag::Short: Read(val.Short); break;
case Tag::Void: break;
case Tag::Boolean: Read(val.Bool); break;
case Tag::String: Read(val.String); break;
case Tag::Thread: Read(val.Thread); break;
case Tag::ThreadGroup: Read(val.ThreadGroup); break;
case Tag::ClassLoader: Read(val.ClassLoader); break;
case Tag::ClassObject: Read(val.ClassObject); break;
}
return *this;
}
template <>
CommandData &CommandData::Write(const value &val)
{
Write((const byte &)val.tag);
switch(val.tag)
{
case Tag::Unknown: break;
case Tag::Array: Write(val.Array); break;
case Tag::Byte: Write(val.Byte); break;
case Tag::Char: Write(val.Char); break;
case Tag::Object: Write(val.Object); break;
case Tag::Float: Write(val.Float); break;
case Tag::Double: Write(val.Double); break;
case Tag::Int: Write(val.Int); break;
case Tag::Long: Write(val.Long); break;
case Tag::Short: Write(val.Short); break;
case Tag::Void: break;
case Tag::Boolean: Write(val.Bool); break;
case Tag::String: Write(val.String); break;
case Tag::Thread: Write(val.Thread); break;
case Tag::ThreadGroup: Write(val.ThreadGroup); break;
case Tag::ClassLoader: Write(val.ClassLoader); break;
case Tag::ClassObject: Write(val.ClassObject); break;
}
return *this;
}
template <>
CommandData &CommandData::Read(Location &loc)
{
Read((byte &)loc.tag);
Read(loc.clss);
Read(loc.meth);
Read(loc.index);
return *this;
}
template <>
CommandData &CommandData::Write(const Location &loc)
{
Write((const byte &)loc.tag);
Write(loc.clss);
Write(loc.meth);
Write(loc.index);
return *this;
}
// objectID variants
template <>
CommandData &CommandData::Read(objectID &id)
{
ReadBytes(&id, objectID::getSize());
id.EndianSwap();
return *this;
}
template <>
CommandData &CommandData::Write(const objectID &id)
{
objectID tmp = id;
tmp.EndianSwap();
WriteBytes(&tmp, objectID::getSize());
return *this;
}
template <>
CommandData &CommandData::Read(referenceTypeID &id)
{
ReadBytes(&id, referenceTypeID::getSize());
id.EndianSwap();
return *this;
}
template <>
CommandData &CommandData::Write(const referenceTypeID &id)
{
referenceTypeID tmp = id;
tmp.EndianSwap();
WriteBytes(&tmp, referenceTypeID::getSize());
return *this;
}
template <>
CommandData &CommandData::Read(methodID &id)
{
ReadBytes(&id, methodID::getSize());
id.EndianSwap();
return *this;
}
template <>
CommandData &CommandData::Write(const methodID &id)
{
methodID tmp = id;
tmp.EndianSwap();
WriteBytes(&tmp, methodID::getSize());
return *this;
}
template <>
CommandData &CommandData::Read(fieldID &id)
{
ReadBytes(&id, fieldID::getSize());
id.EndianSwap();
return *this;
}
template <>
CommandData &CommandData::Write(const fieldID &id)
{
fieldID tmp = id;
tmp.EndianSwap();
WriteBytes(&tmp, fieldID::getSize());
return *this;
}
template <>
CommandData &CommandData::Read(frameID &id)
{
ReadBytes(&id, frameID::getSize());
id.EndianSwap();
return *this;
}
template <>
CommandData &CommandData::Write(const frameID &id)
{
frameID tmp = id;
tmp.EndianSwap();
WriteBytes(&tmp, frameID::getSize());
return *this;
}
};