xdl
Version:
The Expo Development Library
137 lines (132 loc) • 4.63 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.GDBProtocolWriter = exports.GDBProtocolReader = exports.GDBProtocolClient = void 0;
function _debug() {
const data = _interopRequireDefault(require("debug"));
_debug = function () {
return data;
};
return data;
}
function _protocol() {
const data = require("./protocol");
_protocol = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2021 Expo, Inc.
* Copyright (c) 2018 Drifty Co.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const debug = (0, _debug().default)('expo:xdl:ios:lib:protocol:gdb');
const ACK_SUCCESS = '+'.charCodeAt(0);
class GDBProtocolClient extends _protocol().ProtocolClient {
constructor(socket) {
super(socket, new (_protocol().ProtocolReaderFactory)(GDBProtocolReader), new GDBProtocolWriter());
}
}
exports.GDBProtocolClient = GDBProtocolClient;
class GDBProtocolReader extends _protocol().ProtocolReader {
constructor(callback) {
super(1 /* "Header" is '+' or '-' */, callback);
}
onData(data) {
// the GDB protocol does not support body length in its header so we cannot rely on
// the parent implementation to determine when a payload is complete
try {
// if there's data, add it to the existing buffer
this.buffer = data ? Buffer.concat([this.buffer, data]) : this.buffer;
// do we have enough bytes to proceed
if (this.buffer.length < this.headerSize) {
return; // incomplete header, wait for more
}
// first, check the header
if (this.parseHeader(this.buffer) === -1) {
// we have a valid header so check the body. GDB packets will always be a leading '$', data bytes,
// a trailing '#', and a two digit checksum. minimum valid body is the empty response '$#00'
// https://developer.apple.com/library/archive/documentation/DeveloperTools/gdb/gdb/gdb_33.html
const packetData = this.buffer.toString().match('\\$.*#[0-9a-f]{2}');
if (packetData == null) {
return; // incomplete body, wait for more
}
// extract the body and update the buffer
const body = Buffer.from(packetData[0]);
this.buffer = this.buffer.slice(this.headerSize + body.length);
// parse the payload and recurse if there is more data to process
this.callback(this.parseBody(body));
if (this.buffer.length) {
this.onData();
}
}
} catch (err) {
this.callback(null, err);
}
}
parseHeader(data) {
if (data[0] !== ACK_SUCCESS) {
throw new Error('Unsuccessful debugserver response');
} // TODO: retry?
return -1;
}
parseBody(buffer) {
debug(`Response body: ${buffer.toString()}`);
// check for checksum
const checksum = buffer.slice(-3).toString();
if (checksum.match(/#[0-9a-f]{2}/)) {
// remove '$' prefix and checksum
const msg = buffer.slice(1, -3).toString();
if (validateChecksum(checksum, msg)) {
return msg;
} else {
throw new Error('Invalid checksum received from debugserver');
}
} else {
throw new Error("Didn't receive checksum");
}
}
}
exports.GDBProtocolReader = GDBProtocolReader;
class GDBProtocolWriter {
write(socket, msg) {
const {
cmd,
args
} = msg;
debug(`Socket write: ${cmd}, args: ${args}`);
// hex encode and concat all args
const encodedArgs = args.map(arg => Buffer.from(arg).toString('hex')).join().toUpperCase();
const checksumStr = calculateChecksum(cmd + encodedArgs);
const formattedCmd = `$${cmd}${encodedArgs}#${checksumStr}`;
socket.write(formattedCmd);
}
}
// hex value of (sum of cmd chars mod 256)
exports.GDBProtocolWriter = GDBProtocolWriter;
function calculateChecksum(cmdStr) {
let checksum = 0;
for (let i = 0; i < cmdStr.length; i++) {
checksum += cmdStr.charCodeAt(i);
}
let result = (checksum % 256).toString(16);
// pad if necessary
if (result.length === 1) {
result = `0${result}`;
}
return result;
}
function validateChecksum(checksum, msg) {
// remove '#' from checksum
const checksumVal = checksum.slice(1);
// remove '$' from msg and calculate its checksum
const computedChecksum = calculateChecksum(msg);
debug(`Checksum: ${checksumVal}, computed checksum: ${computedChecksum}`);
return checksumVal === computedChecksum;
}
//# sourceMappingURL=gdb.js.map
;