diff --git a/src/Packet.zig b/src/Packet.zig
new file mode 100644
index 0000000..d947fc7
--- /dev/null
+++ b/src/Packet.zig
@@ -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
+};
diff --git a/src/Server.zig b/src/Server.zig
new file mode 100644
index 0000000..72f5d90
--- /dev/null
+++ b/src/Server.zig
@@ -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 =
+ \\
+ \\ armv4t
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+;
+
+// 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;
+}
diff --git a/src/main.zig b/src/main.zig
index f8f4e7f..c440df5 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -1,12 +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 port: u16 = 2424;
-
-const target: []const u8 =
+pub const target: []const u8 =
\\
\\ armv4t
\\
@@ -33,259 +32,17 @@ const target: []const u8 =
;
pub fn main() !void {
- const log = std.log.scoped(.Server);
+ const log = std.log.scoped(.Main);
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer std.debug.assert(!gpa.deinit());
const allocator = gpa.allocator();
- try network.init();
- defer network.deinit();
+ var server = try Server.init();
+ defer server.deinit(allocator);
- var socket = try Socket.create(.ipv4, .tcp);
- defer socket.close();
-
- try socket.bindToPort(port);
- try socket.listen();
-
- var client = try socket.accept();
- defer client.close();
-
- const endpoint = try client.getLocalEndPoint();
- log.info("client connected from {}", .{endpoint});
-
- try gdbStubServer(allocator, client);
+ try server.run(allocator);
log.info("Client disconnected", .{});
}
-
-fn gdbStubServer(allocator: Allocator, client: Socket) !void {
- var buf: [Packet.size]u8 = undefined;
- var previous: ?[]const u8 = null;
- defer if (previous) |packet| allocator.free(packet);
-
- while (true) {
- const len = try client.receive(&buf);
- if (len == 0) break;
-
- switch (try parse(allocator, client, previous, buf[0..len])) {
- .send => |response| {
- if (previous) |packet| allocator.free(packet);
- previous = response;
- },
- .recv_ack => {
- if (previous) |packet| allocator.free(packet);
- previous = null;
- },
- .recv_nack => {},
- }
- }
-}
-
-const Action = union(enum) {
- send: []const u8,
- recv_nack,
- recv_ack,
-};
-
-fn parse(allocator: Allocator, client: Socket, previous: ?[]const u8, input: []const u8) !Action {
- const log = std.log.scoped(.GdbStubParser);
-
- return switch (input[0]) {
- '+' => .recv_ack,
- '-' => blk: {
- if (previous) |packet| {
- log.warn("received nack, resending: \"{s}\"", .{packet});
- _ = try client.send(packet);
- } else {
- log.err("received nack, but we don't recall sending anything", .{});
- }
-
- break :blk .recv_nack;
- },
- '$' => blk: {
- // Packet
- var packet = try Packet.from(allocator, input);
- defer packet.deinit(allocator);
-
- var string = try packet.parse(allocator);
- defer string.deinit(allocator);
-
- const reply = string.inner();
-
- _ = try client.send("+"); // Acknowledge
-
- // deallocated by the caller
- const response = try std.fmt.allocPrint(allocator, "${s}#{x:0>2}", .{ reply, Packet.checksum(reply) });
- _ = try client.send(response);
-
- break :blk .{ .send = response };
- },
- else => std.debug.panic("Unknown: {s}", .{input}),
- };
-}
-
-const Packet = struct {
- const Self = @This();
- const log = std.log.scoped(.Packet);
- const size: 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.size});
- 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
-};