UNPKG

@twilio/plugin-microvisor

Version:

Interact with your Twilio Microvisor devices

632 lines (631 loc) 29.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GdbToProtobuf = exports.GdbPacket = void 0; const tslib_1 = require("tslib"); const memorycache_1 = require("./memorycache"); const memorymap_1 = require("./memorymap"); const node_events_1 = require("node:events"); const proto_1 = require("../../../../proto_gen/proto"); const node_assert_1 = tslib_1.__importDefault(require("node:assert")); // Looks like GDB expects them? var UnixSignals; (function (UnixSignals) { UnixSignals[UnixSignals["SIGINT"] = 2] = "SIGINT"; UnixSignals[UnixSignals["SIGTRAP"] = 5] = "SIGTRAP"; UnixSignals[UnixSignals["SIGSEGV"] = 11] = "SIGSEGV"; })(UnixSignals || (UnixSignals = {})); var GdbPacketType; (function (GdbPacketType) { GdbPacketType[GdbPacketType["ReadRegisters"] = 'g'.charCodeAt(0)] = "ReadRegisters"; GdbPacketType[GdbPacketType["WriteRegisters"] = 'G'.charCodeAt(0)] = "WriteRegisters"; GdbPacketType[GdbPacketType["ReadMemory"] = 'm'.charCodeAt(0)] = "ReadMemory"; GdbPacketType[GdbPacketType["WriteMemory"] = 'M'.charCodeAt(0)] = "WriteMemory"; GdbPacketType[GdbPacketType["Continue"] = 'c'.charCodeAt(0)] = "Continue"; GdbPacketType[GdbPacketType["Step"] = 's'.charCodeAt(0)] = "Step"; GdbPacketType[GdbPacketType["GeneralQueryRead"] = 'q'.charCodeAt(0)] = "GeneralQueryRead"; GdbPacketType[GdbPacketType["GeneralQueryWrite"] = 'Q'.charCodeAt(0)] = "GeneralQueryWrite"; GdbPacketType[GdbPacketType["ExtendedMode"] = '!'.charCodeAt(0)] = "ExtendedMode"; GdbPacketType[GdbPacketType["SetThread"] = 'H'.charCodeAt(0)] = "SetThread"; GdbPacketType[GdbPacketType["GetHaltReason"] = '?'.charCodeAt(0)] = "GetHaltReason"; GdbPacketType[GdbPacketType["InsertBreakpoint"] = 'Z'.charCodeAt(0)] = "InsertBreakpoint"; GdbPacketType[GdbPacketType["RemoveBreakpoint"] = 'z'.charCodeAt(0)] = "RemoveBreakpoint"; GdbPacketType[GdbPacketType["ReadRegister"] = 'p'.charCodeAt(0)] = "ReadRegister"; GdbPacketType[GdbPacketType["WriteRegister"] = 'P'.charCodeAt(0)] = "WriteRegister"; GdbPacketType[GdbPacketType["ThreadStatus"] = 'T'.charCodeAt(0)] = "ThreadStatus"; GdbPacketType[GdbPacketType["ExtendedCommand"] = 'v'.charCodeAt(0)] = "ExtendedCommand"; GdbPacketType[GdbPacketType["KillRequest"] = 'k'.charCodeAt(0)] = "KillRequest"; })(GdbPacketType || (GdbPacketType = {})); const targetXml = '<?xml version="1.0"?><!DOCTYPE target SYSTEM "gdb-target.dtd"><target><feature name="org.gnu.gdb.arm.m-profile"><reg name="r0" bitsize="32"/><reg name="r1" bitsize="32"/><reg name="r2" bitsize="32"/><reg name="r3" bitsize="32"/><reg name="r4" bitsize="32"/><reg name="r5" bitsize="32"/><reg name="r6" bitsize="32"/><reg name="r7" bitsize="32"/><reg name="r8" bitsize="32"/><reg name="r9" bitsize="32"/><reg name="r10" bitsize="32"/><reg name="r11" bitsize="32"/><reg name="r12" bitsize="32"/><reg name="sp" bitsize="32" type="data_ptr"/><reg name="lr" bitsize="32"/><reg name="pc" bitsize="32" type="code_ptr"/><reg name="xpsr" bitsize="32"/></feature><feature name="twimp.ns.regs"><reg name="primask" bitsize="32" regnum="34"/><reg name="basepri" bitsize="32" regnum="35"/><reg name="faultmask" bitsize="32" regnum="36"/><reg name="control" bitsize="32" regnum="37"/><reg name="msp" bitsize="32" regnum="38"/><reg name="psp" bitsize="32" regnum="39"/><reg name="msplim" bitsize="32" regnum="40"/><reg name="psplim" bitsize="32" regnum="41"/><reg name="sfsr" bitsize="32" regnum="42"/><reg name="sfar" bitsize="32" regnum="43"/><reg name="shcsr" bitsize="32" regnum="44"/><reg name="hfsr" bitsize="32" regnum="45"/><reg name="cfsr" bitsize="32" regnum="46"/><reg name="bfar" bitsize="32" regnum="47"/><reg name="mmfar" bitsize="32" regnum="48"/></feature></target>'; class ThreadInfo { constructor(name) { this.name = name; this.fault = proto_1.HaltReasonResponse.HaltReason.NONE; } faultString(fault) { switch (fault) { case proto_1.HaltReasonResponse.HaltReason.NONE: case proto_1.HaltReasonResponse.HaltReason.DEBUG_MONITOR: case proto_1.HaltReasonResponse.HaltReason.HALT_REQUEST: case proto_1.HaltReasonResponse.HaltReason.BREAK: case proto_1.HaltReasonResponse.HaltReason.SINGLE_STEP: return 'running'; case proto_1.HaltReasonResponse.HaltReason.HARD_FAULT: return 'HardFault'; case proto_1.HaltReasonResponse.HaltReason.MEM_FAULT: return 'MemManage Fault'; case proto_1.HaltReasonResponse.HaltReason.BUS_FAULT: return 'BusFault'; case proto_1.HaltReasonResponse.HaltReason.USAGE_FAULT: return 'UsageFault'; case proto_1.HaltReasonResponse.HaltReason.SECURE_FAULT: return 'SecureFault'; default: return 'Unknown'; } } toString() { return `${this.name}: [${this.faultString(this.fault)}]`; } } class GdbPacket { constructor(content) { this.content = ''; // raw (escaped) content of the packet this.content = content; } getType() { return this.content.charCodeAt(1); } getArgs() { return GdbPacket.unescape(this.content).slice(2, -3); } getData() { return GdbPacket.unescape(this.content).slice(1, -3); } static build(data) { let cs = GdbPacket.checksum(data).toString(16).padStart(2, '0'); return new GdbPacket('$' + GdbPacket.escape(data) + '#' + cs); } static escape(data) { let result = ''; for (const c of data) { let cNumber = c.charCodeAt(0); if ('#$}*'.includes(c)) { result += '}'; result += String.fromCharCode(cNumber ^ 0x20); } else { result += c; } } return result; } static unescape(data) { let result = ''; let escape = false; for (const c of data) { let cNumber = c.charCodeAt(0); if (escape) { result += String.fromCharCode(cNumber ^ 0x20); escape = false; } else if (c === '}') { escape = true; } else { result += c; } } return result; } static checksum(data) { return [...data].reduce(((a, b) => (((a + b.charCodeAt(0)) & 0xFF))), 0); } } exports.GdbPacket = GdbPacket; class GdbToProtobuf extends node_events_1.EventEmitter { constructor(protobufToTunnel) { super(); this.packetHandlers = new Map([ [GdbPacketType.ReadRegisters, this.handleReadRegisters.bind(this)], [GdbPacketType.WriteRegisters, this.handleWriteRegisters.bind(this)], [GdbPacketType.ReadMemory, this.handleReadMemory.bind(this)], [GdbPacketType.WriteMemory, this.handleWriteMemory.bind(this)], [GdbPacketType.Continue, this.handleContinue.bind(this)], [GdbPacketType.Step, this.handleStep.bind(this)], [GdbPacketType.GeneralQueryRead, this.handleGeneralQueryRead.bind(this)], [GdbPacketType.GeneralQueryWrite, this.handleGeneralQueryWrite.bind(this)], [GdbPacketType.ExtendedMode, this.handleExtendedMode.bind(this)], [GdbPacketType.SetThread, this.handleSetThread.bind(this)], [GdbPacketType.GetHaltReason, this.handleGetHaltReason.bind(this)], [GdbPacketType.InsertBreakpoint, this.handleInsertBreakpoint.bind(this)], [GdbPacketType.RemoveBreakpoint, this.handleRemoveBreakpoint.bind(this)], [GdbPacketType.ReadRegister, this.handleReadRegister.bind(this)], [GdbPacketType.WriteRegister, this.handleWriteRegister.bind(this)], [GdbPacketType.ExtendedCommand, this.handleExtendedCommand.bind(this)], [GdbPacketType.ThreadStatus, this.handleThreadStatus.bind(this)] ]); this.unsupportedPackets = new Set(); this.protobufToTunnel = protobufToTunnel; this.protobufToTunnel.on('unsolicitedHalt', (data) => { this.emit('unsolicitedHalt', GdbPacket.build(this.formatHaltReason(data))); }); this.protobufToTunnel.on('open', () => { // send a ping when the tunnel is opened: this checks things are working // (and this is important for timely error detection of the end-to-end // crypto), and we can use the opportunity to measure the base latency. const pingStartTime = Date.now(); this.protobufToTunnel.sendPing(() => { const now = Date.now(); this.emit('latency', now - pingStartTime); this.emit('open'); }); }); this.protobufToTunnel.on('close', () => { this.emit('close'); }); this.debugProto = proto_1.DebugProtocol.create(protobufToTunnel.rpcImpl.bind(protobufToTunnel), false, false); this.threadInfo = new ThreadInfo('user code'); this.memMap = new memorymap_1.MemoryMap(memorymap_1.MicrovisorDevices.IMP201); // TODO: once more devices are // supported this should come from // arguments or e.g. HelloResponse this.memMapXml = this.memMap.xml(); this.memCache = new memorycache_1.MemoryCache(GdbToProtobuf.MEMORY_LINE_LENGTH, this.memMap, this.debugProto); } /* * Error codes: * - E99 - generic error when exception is catched * - E90 - bad parameters * - E91 - generic error response from device * - E01-E02 - ReadWriteError from device */ async packet(packet) { let handler = this.packetHandlers.get(packet.getType()); if (handler) { return handler(packet).catch(error => { console.log(error.message); this.emit('debug', error.message); return GdbPacket.build('E99'); }); } let packetType = packet.getType(); this.emit('debug', `No handler for packet of type ${packetType}`); if (!this.unsupportedPackets.has(packetType)) { this.unsupportedPackets.add(packetType); this.emit('telemetry', proto_1.PluginTelemetryType.UNSUPPORTED_GDB_PACKET, String.fromCharCode(packetType)); } return GdbPacket.build(''); } async interrupt() { return this.debugProto.halt({}).then((resp) => { if (resp.haltReason) { this.emit('unsolicitedHalt', GdbPacket.build(this.formatHaltReason(resp.haltReason))); } }); } async hello() { this.emit('debug', `MEMMAPXML: ${this.memMapXml}`); await this.debugProto.hello({}); } async goodbye() { await this.debugProto.goodbye({}); } async telemetry(type, data) { let dataBuffer = new Uint8Array(data.length); for (let i = 0; i < data.length; i++) { dataBuffer[i] = data.charCodeAt(i); } let request = proto_1.PluginTelemetryRequest.create({ type: type, data: dataBuffer }); await this.debugProto.pluginTelemetry(request); } setFault(fault) { this.threadInfo.fault = fault; } formatHaltReason(reason) { let result = 'T'; const regs = reason.registers; this.setFault(proto_1.HaltReasonResponse.HaltReason.NONE); // TODO: doesn't belong here switch (reason.haltReason) { case proto_1.HaltReasonResponse.HaltReason.BREAK: result += UnixSignals.SIGTRAP.toString(16).padStart(2, '0'); result += 'hwbreak'; if (regs && regs.readWrite && regs.readWrite.basicRegisters && regs.readWrite.basicRegisters.pc) { // TODO: look at the actual breakpoints set result += ':' + regs.readWrite.basicRegisters.pc.toString(16).padStart(8, '0'); } result += ';'; break; case proto_1.HaltReasonResponse.HaltReason.SINGLE_STEP: result += UnixSignals.SIGTRAP.toString(16).padStart(2, '0'); break; case proto_1.HaltReasonResponse.HaltReason.HALT_REQUEST: result += UnixSignals.SIGINT.toString(16).padStart(2, '0'); break; case null: /* eslint-disable unicorn/no-useless-undefined */ case undefined: this.setFault(undefined); // TODO: doesn't belong here result += UnixSignals.SIGSEGV.toString(16).padStart(2, '0'); break; default: this.setFault(reason.haltReason); // TODO: doesn't belong here result += UnixSignals.SIGSEGV.toString(16).padStart(2, '0'); break; } if (regs && regs.readWrite && regs.readWrite.basicRegisters) { const basicRegs = new proto_1.registers.BasicRegisters(regs.readWrite.basicRegisters); result += '00:' + GdbToProtobuf.regToHex(basicRegs.r0) + ';'; result += '01:' + GdbToProtobuf.regToHex(basicRegs.r1) + ';'; result += '02:' + GdbToProtobuf.regToHex(basicRegs.r2) + ';'; result += '03:' + GdbToProtobuf.regToHex(basicRegs.r3) + ';'; result += '0C:' + GdbToProtobuf.regToHex(basicRegs.r12) + ';'; result += '0E:' + GdbToProtobuf.regToHex(basicRegs.lr) + ';'; result += '0F:' + GdbToProtobuf.regToHex(basicRegs.pc) + ';'; result += '10:' + GdbToProtobuf.regToHex(basicRegs.xpsr) + ';'; } if (regs && regs.readWrite && regs.readWrite.extendedRegisters) { const extendedRegs = new proto_1.registers.ExtendedRegisters(regs.readWrite.extendedRegisters); result += '04:' + GdbToProtobuf.regToHex(extendedRegs.r4) + ';'; result += '05:' + GdbToProtobuf.regToHex(extendedRegs.r5) + ';'; result += '06:' + GdbToProtobuf.regToHex(extendedRegs.r6) + ';'; result += '07:' + GdbToProtobuf.regToHex(extendedRegs.r7) + ';'; result += '08:' + GdbToProtobuf.regToHex(extendedRegs.r8) + ';'; result += '09:' + GdbToProtobuf.regToHex(extendedRegs.r9) + ';'; result += '0A:' + GdbToProtobuf.regToHex(extendedRegs.r10) + ';'; result += '0B:' + GdbToProtobuf.regToHex(extendedRegs.r11) + ';'; result += '0D:' + GdbToProtobuf.regToHex(extendedRegs.sp) + ';'; } return result; } formatReadRegisters(resp) { let result; if (resp.error === proto_1.ReadWriteError.NONE) { let r0r3 = ''; let r4r11 = ''; let r12 = ''; let sp = ''; let lrPcXpsr = ''; if (resp.readWrite && resp.readWrite.basicRegisters) { const basicRegs = new proto_1.registers.BasicRegisters(resp.readWrite.basicRegisters); r0r3 = GdbToProtobuf.regToHex(basicRegs.r0) + GdbToProtobuf.regToHex(basicRegs.r1) + GdbToProtobuf.regToHex(basicRegs.r2) + GdbToProtobuf.regToHex(basicRegs.r3); r12 = GdbToProtobuf.regToHex(basicRegs.r12); lrPcXpsr = GdbToProtobuf.regToHex(basicRegs.lr) + GdbToProtobuf.regToHex(basicRegs.pc) + GdbToProtobuf.regToHex(basicRegs.xpsr); } else { r0r3 = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; r12 = 'xxxxxxxx'; lrPcXpsr = 'xxxxxxxxxxxxxxxxxxxxxxxx'; } if (resp.readWrite && resp.readWrite.extendedRegisters) { const extendedRegs = new proto_1.registers.ExtendedRegisters(resp.readWrite.extendedRegisters); sp = GdbToProtobuf.regToHex(extendedRegs.sp); r4r11 = GdbToProtobuf.regToHex(extendedRegs.r4) + GdbToProtobuf.regToHex(extendedRegs.r5) + GdbToProtobuf.regToHex(extendedRegs.r6) + GdbToProtobuf.regToHex(extendedRegs.r7) + GdbToProtobuf.regToHex(extendedRegs.r8) + GdbToProtobuf.regToHex(extendedRegs.r9) + GdbToProtobuf.regToHex(extendedRegs.r10) + GdbToProtobuf.regToHex(extendedRegs.r11); } else { r4r11 = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; sp = 'xxxxxxxx'; } result = r0r3 + r4r11 + r12 + sp + lrPcXpsr; } else { let errorString = resp.error.toString(16).padStart(2, '0'); result = `E${errorString}`; } return result; } static regToHex(reg) { return (reg & 0xFF).toString(16).padStart(2, '0') + ((reg >> 8) & 0xFF).toString(16).padStart(2, '0') + ((reg >> 16) & 0xFF).toString(16).padStart(2, '0') + ((reg >> 24) & 0xFF).toString(16).padStart(2, '0'); } static hexToReg(reg) { if (typeof reg === 'undefined') { return Number.NaN; } if (reg.length !== 8) { return Number.NaN; } let regMatch = reg.match(/.{1,2}/g); if (regMatch === null || regMatch.length !== 4) { return Number.NaN; } let result = 0; for (let i = 0; i < 4; i++) { let b = Number.parseInt(regMatch[i], 16); if (Number.isNaN(b)) { return Number.NaN; } result += b << (8 * i); } return result; } async handleReadRegisters(_packet) { return this.debugProto.readRegisters({}).then((resp) => { return GdbPacket.build(this.formatReadRegisters(resp)); }); } async handleWriteRegisters(packet) { let packetArguments = packet.getArgs(); // TODO: should we support XXXX registers? if (packetArguments.length !== 17 * 8) { // TODO: get it from target description, treat incomplete requests properly this.emit('debug', 'Write registers request should contain all 17 registers'); return GdbPacket.build('E90'); } let regMatch = packetArguments.match(/.{1,8}/g); if (regMatch === null) { return GdbPacket.build('E90'); } // eslint-disable-next-line unicorn/no-array-callback-reference let registers = regMatch.map(GdbToProtobuf.hexToReg); if (!registers.every((r) => !Number.isNaN(r))) { this.emit('debug', 'Cannot parse registers'); return GdbPacket.build('E90'); } let request = proto_1.WriteRegistersRequest.create({ registers: { basicRegisters: { r0: registers[0], r1: registers[1], r2: registers[2], r3: registers[3], r12: registers[12], lr: registers[14], pc: registers[15], xpsr: registers[16] }, extendedRegisters: { sp: registers[13], r4: registers[4], r5: registers[5], r6: registers[6], r7: registers[7], r8: registers[8], r9: registers[9], r10: registers[10], r11: registers[11] } } }); return this.debugProto.writeRegisters(request).then((resp) => { if (resp.error === proto_1.ReadWriteError.NONE) { return GdbPacket.build('OK'); } let errorString = resp.error.toString(16).padStart(2, '0'); return GdbPacket.build(`E${errorString}`); }); } async handleReadMemory(packet) { let [address, byteCount] = packet.getArgs().split(',').map((s) => Number.parseInt(s, 16)); if (typeof address === 'undefined' || Number.isNaN(address) || typeof byteCount === 'undefined' || Number.isNaN(byteCount)) { throw new TypeError('Read memory request requires two hex arguments'); } let response = await this.memCache.read(address, byteCount); if (typeof response === 'number') { let errorString = response.toString(10).padStart(2, '0'); return GdbPacket.build(`E${errorString}`); } else { let result = ''; for (const b of response) { result += b.toString(16).padStart(2, '0'); } return GdbPacket.build(result); } } async handleWriteMemory(packet) { let packetArguments = packet.getArgs().split(':'); if (packetArguments.length !== 2) { throw new Error('Write memory request requires data'); } let [address, byteCount] = packetArguments[0].split(',').map((s) => Number.parseInt(s, 16)); if (typeof address === 'undefined' || Number.isNaN(address) || typeof byteCount === 'undefined' || Number.isNaN(byteCount)) { throw new TypeError('Write memory request requires address and length'); } if (2 * byteCount !== packetArguments[1].length) { throw new Error('Byte count and the amount of data don\'t match'); } let chunkNo = 0; while (byteCount > 0) { let chunkSize = (byteCount > 64) ? 64 : byteCount; let memory = new Uint8Array(chunkSize); for (let i = 0; i < chunkSize * 2; i += 2) { let bt = Number.parseInt(packetArguments[1].slice((64 * chunkNo) + i, (64 * chunkNo) + i + 2), 16); (0, node_assert_1.default)(!Number.isNaN(bt)); memory[i / 2] = bt; } let result = await this.memCache.writeChunk(address, memory); if (typeof result === 'number') { let errorString = result.toString(10).padStart(2, '0'); return GdbPacket.build(`E${errorString}`); } byteCount -= chunkSize; address += chunkSize; chunkNo++; } return GdbPacket.build('OK'); } async handleContinue(_packet) { this.memCache.clear(); let promise = this.protobufToTunnel.waitHaltReason(); await this.debugProto.continue({}); return promise.then((r) => { return GdbPacket.build(this.formatHaltReason(r)); }); } async handleStep(_packet) { this.memCache.clear(); let promise = this.protobufToTunnel.waitHaltReason(); await this.debugProto.step({}); return promise.then((r) => { return GdbPacket.build(this.formatHaltReason(r)); }); } readWindow(packetArguments, file) { let packetArgumentsArray = packetArguments.split(':'); let [offset, length] = packetArgumentsArray[packetArgumentsArray.length - 1].split(',').map((a) => Number.parseInt(a, 16)); if (typeof offset !== 'number' || Number.isNaN(offset) || typeof length !== 'number' || Number.isNaN(length)) { return 'E90'; } if (offset >= file.length) { return 'E90'; } let result = (offset + length >= file.length) ? 'l' : 'm'; result += file.slice(offset, offset + length); return result; } async handleGeneralQueryRead(packet) { let packetArguments = packet.getArgs(); if (packetArguments === 'C') { return GdbPacket.build('QC1'); } if (packetArguments === 'fThreadInfo') { return GdbPacket.build('m1'); } if (packetArguments === 'sThreadInfo') { return GdbPacket.build('l'); } if (packetArguments === 'TStatus') { return GdbPacket.build('T0'); } if (packetArguments === 'TfP' || packetArguments === 'TsP' || packetArguments === 'TfV' || packetArguments === 'TsV') { // TODO: tracepoints return GdbPacket.build('l'); } if (packetArguments === 'ThreadExtraInfo,1') { let result = ''; for (const c of this.threadInfo.toString()) { result += c.charCodeAt(0).toString(16).padStart(2, '0'); } return GdbPacket.build(result); } if (packetArguments === 'Symbol::') { return GdbPacket.build('OK'); } if (packetArguments.startsWith('Supported')) { return GdbPacket.build('PacketSize=200;qXfer:memory-map:read+;qXfer:features:read+;hwbreak+'); } if (packetArguments.startsWith('Xfer:features:read:target.xml:')) { return GdbPacket.build(this.readWindow(packetArguments, targetXml)); } if (packetArguments.startsWith('Xfer:memory-map:read::')) { return GdbPacket.build(this.readWindow(packetArguments, this.memMapXml)); } return GdbPacket.build(''); } async handleGeneralQueryWrite(_packet) { return GdbPacket.build(''); } async handleExtendedMode(_packet) { return GdbPacket.build('OK'); } async handleSetThread(_packet) { return GdbPacket.build('OK'); } async handleGetHaltReason(_packet) { return this.debugProto.haltReason({}).then((resp) => { return GdbPacket.build(this.formatHaltReason(resp)); }); } async handleInsertBreakpoint(packet) { let [type, addr, kind] = packet.getArgs().split(','); if (type == '') { return GdbPacket.build('E90'); } if (type !== '0' && type !== '1') { return GdbPacket.build(''); } if (kind !== '2' && kind !== '3' && kind !== '4') { return GdbPacket.build('E90'); } let addrNumber = Number.parseInt(addr, 16); if (Number.isNaN(addrNumber)) { return GdbPacket.build('E90'); } let request = proto_1.InsertBreakpointRequest.create({ address: addrNumber }); return this.debugProto.insertBreakpoint(request).then((resp) => { return GdbPacket.build((resp.success) ? 'OK' : 'E91'); }); } async handleRemoveBreakpoint(packet) { let [type, addr, kind] = packet.getArgs().split(','); if (type == '') { return GdbPacket.build('E90'); } if (type !== '0' && type !== '1') { return GdbPacket.build(''); } if (kind !== '2' && kind !== '3' && kind !== '4') { return GdbPacket.build('E90'); } let addrNumber = Number.parseInt(addr, 16); if (Number.isNaN(addrNumber)) { return GdbPacket.build('E90'); } let request = proto_1.RemoveBreakpointRequest.create({ address: addrNumber }); return this.debugProto.removeBreakpoint(request).then((resp) => { return GdbPacket.build((resp.success) ? 'OK' : 'E91'); }); } async handleReadRegister(packet) { let reg = Number.parseInt(packet.getArgs(), 16); if (Number.isNaN(reg)) { return GdbPacket.build('E90'); } let request = proto_1.ReadRegisterRequest.create({ index: reg }); return this.debugProto.readRegister(request).then((resp) => { if (resp.error === proto_1.ReadWriteError.NONE) { return GdbPacket.build(GdbToProtobuf.regToHex(resp.value)); } let errorString = resp.error.toString(16).padStart(2, '0'); return GdbPacket.build(`E${errorString}`); }); } async handleWriteRegister(packet) { let [reg, value] = packet.getArgs().split('='); let registerNumber = Number.parseInt(reg, 16); let valueNumber = GdbToProtobuf.hexToReg(value); if (typeof registerNumber !== 'number' || Number.isNaN(registerNumber) || typeof valueNumber !== 'number' || Number.isNaN(valueNumber)) { return GdbPacket.build('E90'); } let request = proto_1.WriteRegisterRequest.create({ index: registerNumber, value: valueNumber }); return this.debugProto.writeRegister(request).then((resp) => { if (resp.error === proto_1.ReadWriteError.NONE) { return GdbPacket.build('OK'); } let errorString = resp.error.toString(16).padStart(2, '0'); return GdbPacket.build(`E${errorString}`); }); } async handleThreadStatus(_packet) { return GdbPacket.build('OK'); } async handleExtendedCommand(_packet) { return GdbPacket.build(''); } } exports.GdbToProtobuf = GdbToProtobuf; GdbToProtobuf.MEMORY_LINE_LENGTH = 128;