chore: move structs to separate files
This commit is contained in:
		
							
								
								
									
										166
									
								
								src/Packet.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								src/Packet.zig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
			
		||||
const std = @import("std");
 | 
			
		||||
 | 
			
		||||
const target = @import("Server.zig").target;
 | 
			
		||||
const Allocator = std.mem.Allocator;
 | 
			
		||||
 | 
			
		||||
const Self = @This();
 | 
			
		||||
const log = std.log.scoped(.Packet);
 | 
			
		||||
pub const max_len: usize = 0x1000;
 | 
			
		||||
 | 
			
		||||
contents: []const u8,
 | 
			
		||||
 | 
			
		||||
pub fn from(allocator: Allocator, str: []const u8) !Self {
 | 
			
		||||
    var tokens = std.mem.tokenize(u8, str, "$#");
 | 
			
		||||
    const contents = tokens.next() orelse return error.InvalidPacket;
 | 
			
		||||
 | 
			
		||||
    const chksum_str = tokens.next() orelse return error.MissingCheckSum;
 | 
			
		||||
    const chksum = std.fmt.parseInt(u8, chksum_str, 16) catch return error.InvalidChecksum;
 | 
			
		||||
 | 
			
		||||
    // log.info("Contents: {s}", .{contents});
 | 
			
		||||
 | 
			
		||||
    if (!Self.verify(contents, chksum)) return error.ChecksumMismatch;
 | 
			
		||||
 | 
			
		||||
    return .{ .contents = try allocator.dupe(u8, contents) };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const String = union(enum) {
 | 
			
		||||
    alloc: []const u8,
 | 
			
		||||
    static: []const u8,
 | 
			
		||||
 | 
			
		||||
    pub fn inner(self: *const @This()) []const u8 {
 | 
			
		||||
        return switch (self.*) {
 | 
			
		||||
            .static => |str| str,
 | 
			
		||||
            .alloc => |str| str,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn deinit(self: *@This(), allocator: Allocator) void {
 | 
			
		||||
        switch (self.*) {
 | 
			
		||||
            .alloc => |string| allocator.free(string),
 | 
			
		||||
            .static => {},
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.* = undefined;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub fn parse(self: *Self, allocator: Allocator) !String {
 | 
			
		||||
    switch (self.contents[0]) {
 | 
			
		||||
        // Required
 | 
			
		||||
        '?' => {
 | 
			
		||||
            const ret: Signal = .Trap;
 | 
			
		||||
 | 
			
		||||
            // Deallocated by the caller
 | 
			
		||||
            return .{ .alloc = try std.fmt.allocPrint(allocator, "T{x:0>2}thread:1;", .{@enumToInt(ret)}) };
 | 
			
		||||
        },
 | 
			
		||||
        'g', 'G' => @panic("TODO: Register Access"),
 | 
			
		||||
        'm', 'M' => @panic("TODO: Memory Access"),
 | 
			
		||||
        'c' => @panic("TODO: Continue"),
 | 
			
		||||
        's' => @panic("TODO: Step"),
 | 
			
		||||
 | 
			
		||||
        // Optional
 | 
			
		||||
        'H' => {
 | 
			
		||||
            log.warn("{s}", .{self.contents});
 | 
			
		||||
 | 
			
		||||
            switch (self.contents[1]) {
 | 
			
		||||
                'g', 'c' => return .{ .static = "OK" },
 | 
			
		||||
                else => {
 | 
			
		||||
                    log.warn("Unimplemented: {s}", .{self.contents});
 | 
			
		||||
                    return .{ .static = "" };
 | 
			
		||||
                },
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        'v' => {
 | 
			
		||||
            if (substr(self.contents[1..], "MustReplyEmpty")) {
 | 
			
		||||
                return .{ .static = "" };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            log.warn("Unimplemented: {s}", .{self.contents});
 | 
			
		||||
            return .{ .static = "" };
 | 
			
		||||
        },
 | 
			
		||||
        'q' => {
 | 
			
		||||
            if (self.contents[1] == 'C' and self.contents.len == 2) return .{ .static = "QC1" };
 | 
			
		||||
            if (substr(self.contents[1..], "fThreadInfo")) return .{ .static = "m1" };
 | 
			
		||||
            if (substr(self.contents[1..], "sThreadInfo")) return .{ .static = "l" };
 | 
			
		||||
            if (substr(self.contents[1..], "Attached")) return .{ .static = "1" }; // Tell GDB we're attached to a process
 | 
			
		||||
 | 
			
		||||
            if (substr(self.contents[1..], "Supported")) {
 | 
			
		||||
                const format = "PacketSize={x:};qXfer:features:read+;qXfer:memory-map:read+";
 | 
			
		||||
                // TODO: Anything else?
 | 
			
		||||
 | 
			
		||||
                const ret = try std.fmt.allocPrint(allocator, format, .{Self.max_len});
 | 
			
		||||
                return .{ .alloc = ret };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (substr(self.contents[1..], "Xfer:features:read")) {
 | 
			
		||||
                var tokens = std.mem.tokenize(u8, self.contents[1..], ":,");
 | 
			
		||||
                _ = tokens.next(); // qXfer
 | 
			
		||||
                _ = tokens.next(); // features
 | 
			
		||||
                _ = tokens.next(); // read
 | 
			
		||||
                const annex = tokens.next() orelse return .{ .static = "E00" };
 | 
			
		||||
                const offset_str = tokens.next() orelse return .{ .static = "E00" };
 | 
			
		||||
                const length_str = tokens.next() orelse return .{ .static = "E00" };
 | 
			
		||||
 | 
			
		||||
                if (std.mem.eql(u8, annex, "target.xml")) {
 | 
			
		||||
                    log.info("Providing ARMv4T target description", .{});
 | 
			
		||||
 | 
			
		||||
                    const offset = try std.fmt.parseInt(usize, offset_str, 16);
 | 
			
		||||
                    const length = try std.fmt.parseInt(usize, length_str, 16);
 | 
			
		||||
 | 
			
		||||
                    // + 2 to account for the "m " in the response
 | 
			
		||||
                    // subtract offset so that the allocated buffer isn't
 | 
			
		||||
                    // larger than it needs to be TODO: Test this?
 | 
			
		||||
                    const len = @min(length, (target.len + 1) - offset);
 | 
			
		||||
                    const ret = try allocator.alloc(u8, len);
 | 
			
		||||
 | 
			
		||||
                    ret[0] = if (ret.len < length) 'l' else 'm';
 | 
			
		||||
                    std.mem.copy(u8, ret[1..], target[offset..]);
 | 
			
		||||
 | 
			
		||||
                    return .{ .alloc = ret };
 | 
			
		||||
                } else {
 | 
			
		||||
                    log.err("Unexpected Annex: {s}", .{annex});
 | 
			
		||||
                    return .{ .static = "E00" };
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return .{ .static = "" };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            log.warn("Unimplemented: {s}", .{self.contents});
 | 
			
		||||
            return .{ .static = "" };
 | 
			
		||||
        },
 | 
			
		||||
        else => {
 | 
			
		||||
            log.warn("Unknown:  {s}", .{self.contents});
 | 
			
		||||
 | 
			
		||||
            return .{ .static = "" };
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn substr(haystack: []const u8, needle: []const u8) bool {
 | 
			
		||||
    return std.mem.indexOf(u8, haystack, needle) != null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn deinit(self: *Self, allocator: Allocator) void {
 | 
			
		||||
    allocator.free(self.contents);
 | 
			
		||||
    self.* = undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn checksum(input: []const u8) u8 {
 | 
			
		||||
    var sum: usize = 0;
 | 
			
		||||
    for (input) |char| sum += char;
 | 
			
		||||
 | 
			
		||||
    return @truncate(u8, sum);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn verify(input: []const u8, chksum: u8) bool {
 | 
			
		||||
    return Self.checksum(input) == chksum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Signal = enum(u32) {
 | 
			
		||||
    Hup, // Hangup
 | 
			
		||||
    Int, // Interrupt
 | 
			
		||||
    Quit, // Quit
 | 
			
		||||
    Ill, // Illegal Instruction
 | 
			
		||||
    Trap, // Trace/Breakponit trap
 | 
			
		||||
    Abrt, // Aborted
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										141
									
								
								src/Server.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/Server.zig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
			
		||||
const std = @import("std");
 | 
			
		||||
const network = @import("network");
 | 
			
		||||
const Packet = @import("Packet.zig");
 | 
			
		||||
 | 
			
		||||
const Socket = network.Socket;
 | 
			
		||||
const Allocator = std.mem.Allocator;
 | 
			
		||||
 | 
			
		||||
const Self = @This();
 | 
			
		||||
const log = std.log.scoped(.Server);
 | 
			
		||||
const port: u16 = 2424;
 | 
			
		||||
 | 
			
		||||
pub const target: []const u8 =
 | 
			
		||||
    \\<target version="1.0">
 | 
			
		||||
    \\    <architecture>armv4t</architecture>
 | 
			
		||||
    \\    <feature name="org.gnu.gdb.arm.core">
 | 
			
		||||
    \\        <reg name="r0" bitsize="32" type="uint32"/>
 | 
			
		||||
    \\        <reg name="r1" bitsize="32" type="uint32"/>
 | 
			
		||||
    \\        <reg name="r2" bitsize="32" type="uint32"/>
 | 
			
		||||
    \\        <reg name="r3" bitsize="32" type="uint32"/>
 | 
			
		||||
    \\        <reg name="r4" bitsize="32" type="uint32"/>
 | 
			
		||||
    \\        <reg name="r5" bitsize="32" type="uint32"/>
 | 
			
		||||
    \\        <reg name="r6" bitsize="32" type="uint32"/>
 | 
			
		||||
    \\        <reg name="r7" bitsize="32" type="uint32"/>
 | 
			
		||||
    \\        <reg name="r8" bitsize="32" type="uint32"/>
 | 
			
		||||
    \\        <reg name="r9" bitsize="32" type="uint32"/>
 | 
			
		||||
    \\        <reg name="r10" bitsize="32" type="uint32"/>
 | 
			
		||||
    \\        <reg name="r11" bitsize="32" type="uint32"/>
 | 
			
		||||
    \\        <reg name="r12" bitsize="32" type="uint32"/>
 | 
			
		||||
    \\        <reg name="sp" bitsize="32" type="data_ptr"/>
 | 
			
		||||
    \\        <reg name="lr" bitsize="32"/>
 | 
			
		||||
    \\        <reg name="pc" bitsize="32" type="code_ptr"/>
 | 
			
		||||
    \\
 | 
			
		||||
    \\        <reg name="cpsr" bitsize="32" regnum="25"/>
 | 
			
		||||
    \\    </feature>
 | 
			
		||||
    \\</target>
 | 
			
		||||
;
 | 
			
		||||
 | 
			
		||||
// FIXME: Shouldn't this be a Packet Struct?
 | 
			
		||||
pkt_cache: ?[]const u8 = null,
 | 
			
		||||
 | 
			
		||||
client: Socket,
 | 
			
		||||
_socket: Socket,
 | 
			
		||||
 | 
			
		||||
pub fn init() !Self {
 | 
			
		||||
    try network.init();
 | 
			
		||||
 | 
			
		||||
    var socket = try Socket.create(.ipv4, .tcp);
 | 
			
		||||
    try socket.bindToPort(port);
 | 
			
		||||
    try socket.listen();
 | 
			
		||||
 | 
			
		||||
    var client = try socket.accept(); // TODO: This blocks, is this OK?
 | 
			
		||||
 | 
			
		||||
    const endpoint = try client.getLocalEndPoint();
 | 
			
		||||
    log.info("client connected from {}", .{endpoint});
 | 
			
		||||
 | 
			
		||||
    return .{ ._socket = socket, .client = client };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn deinit(self: *Self, allocator: Allocator) void {
 | 
			
		||||
    self.reset(allocator);
 | 
			
		||||
 | 
			
		||||
    self.client.close();
 | 
			
		||||
    self._socket.close();
 | 
			
		||||
    network.deinit();
 | 
			
		||||
 | 
			
		||||
    self.* = undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Action = union(enum) {
 | 
			
		||||
    nothing,
 | 
			
		||||
    send: []const u8,
 | 
			
		||||
    retry,
 | 
			
		||||
    ack,
 | 
			
		||||
    nack,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub fn run(self: *Self, allocator: Allocator) !void {
 | 
			
		||||
    var buf: [Packet.max_len]u8 = undefined;
 | 
			
		||||
 | 
			
		||||
    while (true) {
 | 
			
		||||
        const len = try self.client.receive(&buf);
 | 
			
		||||
        if (len == 0) break;
 | 
			
		||||
 | 
			
		||||
        const action = try Self.parse(allocator, buf[0..len]);
 | 
			
		||||
        try self.send(allocator, action);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse(allocator: Allocator, input: []const u8) !Action {
 | 
			
		||||
    return switch (input[0]) {
 | 
			
		||||
        '+' => .nothing,
 | 
			
		||||
        '-' => .retry,
 | 
			
		||||
        '$' => blk: {
 | 
			
		||||
            // Packet
 | 
			
		||||
            var packet = Packet.from(allocator, input) catch return .nack;
 | 
			
		||||
            defer packet.deinit(allocator);
 | 
			
		||||
 | 
			
		||||
            var string = packet.parse(allocator) catch return .nack;
 | 
			
		||||
            defer string.deinit(allocator);
 | 
			
		||||
 | 
			
		||||
            const reply = string.inner();
 | 
			
		||||
 | 
			
		||||
            // deallocated by the caller
 | 
			
		||||
            const response = try std.fmt.allocPrint(allocator, "${s}#{x:0>2}", .{ reply, Packet.checksum(reply) });
 | 
			
		||||
 | 
			
		||||
            break :blk .{ .send = response };
 | 
			
		||||
        },
 | 
			
		||||
        else => std.debug.panic("Unknown: {s}", .{input}),
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn send(self: *Self, allocator: Allocator, action: Action) !void {
 | 
			
		||||
    switch (action) {
 | 
			
		||||
        .send => |pkt| {
 | 
			
		||||
            _ = try self.client.send("+"); // ACK
 | 
			
		||||
            _ = try self.client.send(pkt);
 | 
			
		||||
 | 
			
		||||
            self.reset(allocator);
 | 
			
		||||
            self.pkt_cache = pkt;
 | 
			
		||||
        },
 | 
			
		||||
        .retry => {
 | 
			
		||||
            log.warn("received nack, resending: \"{?s}\"", .{self.pkt_cache});
 | 
			
		||||
 | 
			
		||||
            if (self.pkt_cache) |pkt| _ = try self.client.send(pkt); // FIXME: is an ack to a nack necessary?
 | 
			
		||||
        },
 | 
			
		||||
        .ack => {
 | 
			
		||||
            _ = try self.client.send("+");
 | 
			
		||||
            self.reset(allocator);
 | 
			
		||||
        },
 | 
			
		||||
        .nack => {
 | 
			
		||||
            _ = try self.client.send("-");
 | 
			
		||||
            self.reset(allocator);
 | 
			
		||||
        },
 | 
			
		||||
        .nothing => self.reset(allocator),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn reset(self: *Self, allocator: Allocator) void {
 | 
			
		||||
    if (self.pkt_cache) |pkt| allocator.free(pkt);
 | 
			
		||||
    self.pkt_cache = null;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										278
									
								
								src/main.zig
									
									
									
									
									
								
							
							
						
						
									
										278
									
								
								src/main.zig
									
									
									
									
									
								
							@@ -1,10 +1,11 @@
 | 
			
		||||
const std = @import("std");
 | 
			
		||||
const network = @import("network");
 | 
			
		||||
const Server = @import("Server.zig");
 | 
			
		||||
 | 
			
		||||
const Allocator = std.mem.Allocator;
 | 
			
		||||
const Socket = network.Socket;
 | 
			
		||||
 | 
			
		||||
const target: []const u8 =
 | 
			
		||||
pub const target: []const u8 =
 | 
			
		||||
    \\<target version="1.0">
 | 
			
		||||
    \\    <architecture>armv4t</architecture>
 | 
			
		||||
    \\    <feature name="org.gnu.gdb.arm.core">
 | 
			
		||||
@@ -45,278 +46,3 @@ pub fn main() !void {
 | 
			
		||||
 | 
			
		||||
    log.info("Client disconnected", .{});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Server = struct {
 | 
			
		||||
    const Self = @This();
 | 
			
		||||
    const log = std.log.scoped(.Server);
 | 
			
		||||
    const port: u16 = 2424;
 | 
			
		||||
 | 
			
		||||
    // FIXME: Shouldn't this be a Packet Struct?
 | 
			
		||||
    pkt_cache: ?[]const u8 = null,
 | 
			
		||||
 | 
			
		||||
    client: Socket,
 | 
			
		||||
    _socket: Socket,
 | 
			
		||||
 | 
			
		||||
    pub fn init() !Self {
 | 
			
		||||
        try network.init();
 | 
			
		||||
 | 
			
		||||
        var socket = try Socket.create(.ipv4, .tcp);
 | 
			
		||||
        try socket.bindToPort(port);
 | 
			
		||||
        try socket.listen();
 | 
			
		||||
 | 
			
		||||
        var client = try socket.accept(); // TODO: This blocks, is this OK?
 | 
			
		||||
 | 
			
		||||
        const endpoint = try client.getLocalEndPoint();
 | 
			
		||||
        log.info("client connected from {}", .{endpoint});
 | 
			
		||||
 | 
			
		||||
        return .{ ._socket = socket, .client = client };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn deinit(self: *Self, allocator: Allocator) void {
 | 
			
		||||
        self.reset(allocator);
 | 
			
		||||
 | 
			
		||||
        self.client.close();
 | 
			
		||||
        self._socket.close();
 | 
			
		||||
        network.deinit();
 | 
			
		||||
 | 
			
		||||
        self.* = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const Action = union(enum) {
 | 
			
		||||
        nothing,
 | 
			
		||||
        send: []const u8,
 | 
			
		||||
        retry,
 | 
			
		||||
        ack,
 | 
			
		||||
        nack,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pub fn run(self: *Self, allocator: Allocator) !void {
 | 
			
		||||
        var buf: [Packet.max_len]u8 = undefined;
 | 
			
		||||
 | 
			
		||||
        while (true) {
 | 
			
		||||
            const len = try self.client.receive(&buf);
 | 
			
		||||
            if (len == 0) break;
 | 
			
		||||
 | 
			
		||||
            const action = try Self.parse(allocator, buf[0..len]);
 | 
			
		||||
            try self.send(allocator, action);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn parse(allocator: Allocator, input: []const u8) !Action {
 | 
			
		||||
        return switch (input[0]) {
 | 
			
		||||
            '+' => .nothing,
 | 
			
		||||
            '-' => .retry,
 | 
			
		||||
            '$' => blk: {
 | 
			
		||||
                // Packet
 | 
			
		||||
                var packet = Packet.from(allocator, input) catch return .nack;
 | 
			
		||||
                defer packet.deinit(allocator);
 | 
			
		||||
 | 
			
		||||
                var string = packet.parse(allocator) catch return .nack;
 | 
			
		||||
                defer string.deinit(allocator);
 | 
			
		||||
 | 
			
		||||
                const reply = string.inner();
 | 
			
		||||
 | 
			
		||||
                // deallocated by the caller
 | 
			
		||||
                const response = try std.fmt.allocPrint(allocator, "${s}#{x:0>2}", .{ reply, Packet.checksum(reply) });
 | 
			
		||||
 | 
			
		||||
                break :blk .{ .send = response };
 | 
			
		||||
            },
 | 
			
		||||
            else => std.debug.panic("Unknown: {s}", .{input}),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn send(self: *Self, allocator: Allocator, action: Action) !void {
 | 
			
		||||
        switch (action) {
 | 
			
		||||
            .send => |pkt| {
 | 
			
		||||
                _ = try self.client.send("+"); // ACK
 | 
			
		||||
                _ = try self.client.send(pkt);
 | 
			
		||||
 | 
			
		||||
                self.reset(allocator);
 | 
			
		||||
                self.pkt_cache = pkt;
 | 
			
		||||
            },
 | 
			
		||||
            .retry => {
 | 
			
		||||
                log.warn("received nack, resending: \"{?s}\"", .{self.pkt_cache});
 | 
			
		||||
 | 
			
		||||
                if (self.pkt_cache) |pkt| _ = try self.client.send(pkt); // FIXME: is an ack to a nack necessary?
 | 
			
		||||
            },
 | 
			
		||||
            .ack => {
 | 
			
		||||
                _ = try self.client.send("+");
 | 
			
		||||
                self.reset(allocator);
 | 
			
		||||
            },
 | 
			
		||||
            .nack => {
 | 
			
		||||
                _ = try self.client.send("-");
 | 
			
		||||
                self.reset(allocator);
 | 
			
		||||
            },
 | 
			
		||||
            .nothing => self.reset(allocator),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn reset(self: *Self, allocator: Allocator) void {
 | 
			
		||||
        if (self.pkt_cache) |pkt| allocator.free(pkt);
 | 
			
		||||
        self.pkt_cache = null;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Packet = struct {
 | 
			
		||||
    const Self = @This();
 | 
			
		||||
    const log = std.log.scoped(.Packet);
 | 
			
		||||
    const max_len: usize = 0x1000;
 | 
			
		||||
 | 
			
		||||
    contents: []const u8,
 | 
			
		||||
 | 
			
		||||
    pub fn from(allocator: Allocator, str: []const u8) !Self {
 | 
			
		||||
        var tokens = std.mem.tokenize(u8, str, "$#");
 | 
			
		||||
        const contents = tokens.next() orelse return error.InvalidPacket;
 | 
			
		||||
 | 
			
		||||
        const chksum_str = tokens.next() orelse return error.MissingCheckSum;
 | 
			
		||||
        const chksum = std.fmt.parseInt(u8, chksum_str, 16) catch return error.InvalidChecksum;
 | 
			
		||||
 | 
			
		||||
        // log.info("Contents: {s}", .{contents});
 | 
			
		||||
 | 
			
		||||
        if (!Self.verify(contents, chksum)) return error.ChecksumMismatch;
 | 
			
		||||
 | 
			
		||||
        return .{ .contents = try allocator.dupe(u8, contents) };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const String = union(enum) {
 | 
			
		||||
        alloc: []const u8,
 | 
			
		||||
        static: []const u8,
 | 
			
		||||
 | 
			
		||||
        pub fn inner(self: *const @This()) []const u8 {
 | 
			
		||||
            return switch (self.*) {
 | 
			
		||||
                .static => |str| str,
 | 
			
		||||
                .alloc => |str| str,
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pub fn deinit(self: *@This(), allocator: Allocator) void {
 | 
			
		||||
            switch (self.*) {
 | 
			
		||||
                .alloc => |string| allocator.free(string),
 | 
			
		||||
                .static => {},
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            self.* = undefined;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pub fn parse(self: *Self, allocator: Allocator) !String {
 | 
			
		||||
        switch (self.contents[0]) {
 | 
			
		||||
            // Required
 | 
			
		||||
            '?' => {
 | 
			
		||||
                const ret: Signal = .Trap;
 | 
			
		||||
 | 
			
		||||
                // Deallocated by the caller
 | 
			
		||||
                return .{ .alloc = try std.fmt.allocPrint(allocator, "T{x:0>2}thread:1;", .{@enumToInt(ret)}) };
 | 
			
		||||
            },
 | 
			
		||||
            'g', 'G' => @panic("TODO: Register Access"),
 | 
			
		||||
            'm', 'M' => @panic("TODO: Memory Access"),
 | 
			
		||||
            'c' => @panic("TODO: Continue"),
 | 
			
		||||
            's' => @panic("TODO: Step"),
 | 
			
		||||
 | 
			
		||||
            // Optional
 | 
			
		||||
            'H' => {
 | 
			
		||||
                log.warn("{s}", .{self.contents});
 | 
			
		||||
 | 
			
		||||
                switch (self.contents[1]) {
 | 
			
		||||
                    'g', 'c' => return .{ .static = "OK" },
 | 
			
		||||
                    else => {
 | 
			
		||||
                        log.warn("Unimplemented: {s}", .{self.contents});
 | 
			
		||||
                        return .{ .static = "" };
 | 
			
		||||
                    },
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            'v' => {
 | 
			
		||||
                if (substr(self.contents[1..], "MustReplyEmpty")) {
 | 
			
		||||
                    return .{ .static = "" };
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                log.warn("Unimplemented: {s}", .{self.contents});
 | 
			
		||||
                return .{ .static = "" };
 | 
			
		||||
            },
 | 
			
		||||
            'q' => {
 | 
			
		||||
                if (self.contents[1] == 'C' and self.contents.len == 2) return .{ .static = "QC1" };
 | 
			
		||||
                if (substr(self.contents[1..], "fThreadInfo")) return .{ .static = "m1" };
 | 
			
		||||
                if (substr(self.contents[1..], "sThreadInfo")) return .{ .static = "l" };
 | 
			
		||||
                if (substr(self.contents[1..], "Attached")) return .{ .static = "1" }; // Tell GDB we're attached to a process
 | 
			
		||||
 | 
			
		||||
                if (substr(self.contents[1..], "Supported")) {
 | 
			
		||||
                    const format = "PacketSize={x:};qXfer:features:read+;qXfer:memory-map:read+";
 | 
			
		||||
                    // TODO: Anything else?
 | 
			
		||||
 | 
			
		||||
                    const ret = try std.fmt.allocPrint(allocator, format, .{Packet.max_len});
 | 
			
		||||
                    return .{ .alloc = ret };
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (substr(self.contents[1..], "Xfer:features:read")) {
 | 
			
		||||
                    var tokens = std.mem.tokenize(u8, self.contents[1..], ":,");
 | 
			
		||||
                    _ = tokens.next(); // qXfer
 | 
			
		||||
                    _ = tokens.next(); // features
 | 
			
		||||
                    _ = tokens.next(); // read
 | 
			
		||||
                    const annex = tokens.next() orelse return .{ .static = "E00" };
 | 
			
		||||
                    const offset_str = tokens.next() orelse return .{ .static = "E00" };
 | 
			
		||||
                    const length_str = tokens.next() orelse return .{ .static = "E00" };
 | 
			
		||||
 | 
			
		||||
                    if (std.mem.eql(u8, annex, "target.xml")) {
 | 
			
		||||
                        log.info("Providing ARMv4T target description", .{});
 | 
			
		||||
 | 
			
		||||
                        const offset = try std.fmt.parseInt(usize, offset_str, 16);
 | 
			
		||||
                        const length = try std.fmt.parseInt(usize, length_str, 16);
 | 
			
		||||
 | 
			
		||||
                        // + 2 to account for the "m " in the response
 | 
			
		||||
                        // subtract offset so that the allocated buffer isn't
 | 
			
		||||
                        // larger than it needs to be TODO: Test this?
 | 
			
		||||
                        const len = @min(length, (target.len + 1) - offset);
 | 
			
		||||
                        const ret = try allocator.alloc(u8, len);
 | 
			
		||||
 | 
			
		||||
                        ret[0] = if (ret.len < length) 'l' else 'm';
 | 
			
		||||
                        std.mem.copy(u8, ret[1..], target[offset..]);
 | 
			
		||||
 | 
			
		||||
                        return .{ .alloc = ret };
 | 
			
		||||
                    } else {
 | 
			
		||||
                        log.err("Unexpected Annex: {s}", .{annex});
 | 
			
		||||
                        return .{ .static = "E00" };
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return .{ .static = "" };
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                log.warn("Unimplemented: {s}", .{self.contents});
 | 
			
		||||
                return .{ .static = "" };
 | 
			
		||||
            },
 | 
			
		||||
            else => {
 | 
			
		||||
                log.warn("Unknown:  {s}", .{self.contents});
 | 
			
		||||
 | 
			
		||||
                return .{ .static = "" };
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn substr(haystack: []const u8, needle: []const u8) bool {
 | 
			
		||||
        return std.mem.indexOf(u8, haystack, needle) != null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn deinit(self: *Self, allocator: Allocator) void {
 | 
			
		||||
        allocator.free(self.contents);
 | 
			
		||||
        self.* = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn checksum(input: []const u8) u8 {
 | 
			
		||||
        var sum: usize = 0;
 | 
			
		||||
        for (input) |char| sum += char;
 | 
			
		||||
 | 
			
		||||
        return @truncate(u8, sum);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn verify(input: []const u8, chksum: u8) bool {
 | 
			
		||||
        return Self.checksum(input) == chksum;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Signal = enum(u32) {
 | 
			
		||||
    Hup, // Hangup
 | 
			
		||||
    Int, // Interrupt
 | 
			
		||||
    Quit, // Quit
 | 
			
		||||
    Ill, // Illegal Instruction
 | 
			
		||||
    Trap, // Trace/Breakponit trap
 | 
			
		||||
    Abrt, // Aborted
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user