chore: upgrade to zig v0.15.1
This commit is contained in:
70
build.zig
70
build.zig
@@ -1,35 +1,63 @@
|
||||
const std = @import("std");
|
||||
|
||||
// Although this function looks imperative, note that its job is to
|
||||
// declaratively construct a build graph that will be executed by an external
|
||||
// runner.
|
||||
// Although this function looks imperative, it does not perform the build
|
||||
// directly and instead it mutates the build graph (`b`) that will be then
|
||||
// executed by an external runner. The functions in `std.Build` implement a DSL
|
||||
// for defining build steps and express dependencies between them, allowing the
|
||||
// build runner to parallelize the build automatically (and the cache system to
|
||||
// know when a step doesn't need to be re-run).
|
||||
pub fn build(b: *std.Build) void {
|
||||
// Standard target options allows the person running `zig build` to choose
|
||||
// Standard target options allow the person running `zig build` to choose
|
||||
// what target to build for. Here we do not override the defaults, which
|
||||
// means any target is allowed, and the default is native. Other options
|
||||
// for restricting supported target set are available.
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
// Standard optimization options allow the person running `zig build` to select
|
||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
_ = b.addModule("zba-gdbstub", .{ .root_source_file = b.path("src/lib.zig") });
|
||||
|
||||
// Creates a step for unit testing. This only builds the test executable
|
||||
// but does not run it.
|
||||
const lib_unit_tests = b.addTest(.{
|
||||
// This creates a module, which represents a collection of source files alongside
|
||||
// some compilation options, such as optimization mode and linked system libraries.
|
||||
// Zig modules are the preferred way of making Zig code available to consumers.
|
||||
// addModule defines a module that we intend to make available for importing
|
||||
// to our consumers. We must give it a name because a Zig package can expose
|
||||
// multiple modules and consumers will need to be able to specify which
|
||||
// module they want to access.
|
||||
const mod = b.addModule("zba_gdbstub", .{
|
||||
// The root source file is the "entry point" of this module. Users of
|
||||
// this module will only be able to access public declarations contained
|
||||
// in this file, which means that if you have declarations that you
|
||||
// intend to expose to consumers that were defined in other files part
|
||||
// of this module, you will have to make sure to re-export them from
|
||||
// the root file.
|
||||
.root_source_file = b.path("src/lib.zig"),
|
||||
// Later on we'll use this module as the root module of a test executable
|
||||
// which requires us to specify a target.
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
|
||||
// Creates an executable that will run `test` blocks from the provided module.
|
||||
// Here `mod` needs to define a target, which is why earlier we made sure to
|
||||
// set the releative field.
|
||||
const mod_tests = b.addTest(.{
|
||||
.root_module = mod,
|
||||
});
|
||||
|
||||
// Similar to creating the run step earlier, this exposes a `test` step to
|
||||
// the `zig build --help` menu, providing a way for the user to request
|
||||
// running the unit tests.
|
||||
const test_step = b.step("test", "Run unit tests");
|
||||
test_step.dependOn(&run_lib_unit_tests.step);
|
||||
// A run step that will run the test executable.
|
||||
const run_mod_tests = b.addRunArtifact(mod_tests);
|
||||
|
||||
// A top level step for running all tests. dependOn can be called multiple
|
||||
// times and since the two run steps do not depend on one another, this will
|
||||
// make the two of them run in parallel.
|
||||
const test_step = b.step("test", "Run tests");
|
||||
test_step.dependOn(&run_mod_tests.step);
|
||||
|
||||
// Just like flags, top level steps are also listed in the `--help` menu.
|
||||
//
|
||||
// The Zig build system is entirely implemented in userland, which means
|
||||
// that it cannot hook into private compiler APIs. All compilation work
|
||||
// orchestrated by the build system will result in other Zig compiler
|
||||
// subcommands being invoked with the right flags defined. You can observe
|
||||
// these invocations when one fails (or you pass a flag to increase
|
||||
// verbosity) to validate assumptions and diagnose problems.
|
||||
//
|
||||
// Lastly, the Zig build system is relatively simple and self-contained,
|
||||
// and reading its source code will allow you to master it.
|
||||
}
|
||||
|
||||
@@ -6,17 +6,26 @@
|
||||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = "zba-gdbstub",
|
||||
|
||||
.name = .zba_gdbstub,
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.1.0",
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
// with this value.
|
||||
//.minimum_zig_version = "0.11.0",
|
||||
|
||||
.version = "0.0.0",
|
||||
// Together with name, this represents a globally unique package
|
||||
// identifier. This field is generated by the Zig toolchain when the
|
||||
// package is first created, and then *never changes*. This allows
|
||||
// unambiguous detection of one package being an updated version of
|
||||
// another.
|
||||
//
|
||||
// When forking a Zig project, this id should be regenerated (delete the
|
||||
// field and run `zig build`) if the upstream project is still maintained.
|
||||
// Otherwise, the fork is *hostile*, attempting to take control over the
|
||||
// original project's identity. Thus it is recommended to leave the comment
|
||||
// on the following line intact, so that it shows up in code reviews that
|
||||
// modify the field.
|
||||
.fingerprint = 0x8006426818f63728, // Changing this has security and trust implications.
|
||||
// Tracks the earliest Zig version that the package considers to be a
|
||||
// supported use case.
|
||||
.minimum_zig_version = "0.15.1",
|
||||
// This field is optional.
|
||||
// Each dependency must either provide a `url` and `hash`, or a `path`.
|
||||
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
|
||||
@@ -27,7 +36,8 @@
|
||||
//.example = .{
|
||||
// // When updating this field to a new URL, be sure to delete the corresponding
|
||||
// // `hash`, otherwise you are communicating that you expect to find the old hash at
|
||||
// // the new URL.
|
||||
// // the new URL. If the contents of a URL change this will result in a hash mismatch
|
||||
// // which will prevent zig from using it.
|
||||
// .url = "https://example.com/foo.tar.gz",
|
||||
//
|
||||
// // This is computed from the file contents of the directory of files that is
|
||||
@@ -45,14 +55,13 @@
|
||||
// // build root. In this case the package's hash is irrelevant and therefore not
|
||||
// // computed. This field and `url` are mutually exclusive.
|
||||
// .path = "foo",
|
||||
|
||||
//
|
||||
// // When this is set to `true`, a package is declared to be lazily
|
||||
// // fetched. This makes the dependency only get fetched if it is
|
||||
// // actually used.
|
||||
// .lazy = false,
|
||||
//},
|
||||
},
|
||||
|
||||
// Specifies the set of files and directories that are included in this package.
|
||||
// Only files and directories listed here are included in the `hash` that
|
||||
// is computed for this package. Only files listed here will remain on disk
|
||||
|
||||
@@ -12,7 +12,7 @@ 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, "$#");
|
||||
var tokens = std.mem.tokenizeAny(u8, str, "$#");
|
||||
const contents = tokens.next() orelse return error.InvalidPacket;
|
||||
|
||||
const chksum_str = tokens.next() orelse return error.MissingCheckSum;
|
||||
@@ -63,7 +63,9 @@ pub fn parse(self: *Self, allocator: Allocator, state: *Server.State, emu: *Emul
|
||||
|
||||
// writes the formatted integer to the buffer, returns a slice to the buffer but we ignore that
|
||||
// GDB also expects the bytes to be in the opposite order for whatever reason
|
||||
_ = std.fmt.bufPrintIntToSlice(ret[i * 8 ..][0..8], @byteSwap(reg), 16, .lower, .{ .fill = '0', .width = 8 });
|
||||
|
||||
_ = try std.fmt.bufPrint(ret[i * 8 ..][0..8], "{x:0>8}", .{@byteSwap(reg)});
|
||||
// _ = std.fmt.bufPrintIntToSlice(ret[i * 8 ..][0..8], @byteSwap(reg), 16, .lower, .{ .fill = '0', .width = 8 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +73,7 @@ pub fn parse(self: *Self, allocator: Allocator, state: *Server.State, emu: *Emul
|
||||
},
|
||||
'G' => @panic("TODO: Register Write"),
|
||||
'm' => {
|
||||
var tokens = std.mem.tokenize(u8, self.contents[1..], ",");
|
||||
var tokens = std.mem.tokenizeAny(u8, self.contents[1..], ",");
|
||||
const addr_str = tokens.next() orelse return error.InvalidPacket;
|
||||
const length_str = tokens.next() orelse return error.InvalidPacket;
|
||||
|
||||
@@ -84,14 +86,16 @@ pub fn parse(self: *Self, allocator: Allocator, state: *Server.State, emu: *Emul
|
||||
var i: u32 = 0;
|
||||
while (i < len) : (i += 1) {
|
||||
// writes the formatted integer to the buffer, returns a slice to the buffer but we ignore that
|
||||
_ = std.fmt.bufPrintIntToSlice(ret[i * 2 ..][0..2], emu.read(addr + i), 16, .lower, .{ .fill = '0', .width = 2 });
|
||||
|
||||
_ = try std.fmt.bufPrint(ret[i * 2 ..][0..2], "{x:0>2}", .{emu.read(addr + i)});
|
||||
// _ = std.fmt.bufPrintIntToSlice(ret[i * 2 ..][0..2], emu.read(addr + i), 16, .lower, .{ .fill = '0', .width = 2 });
|
||||
}
|
||||
}
|
||||
|
||||
return .{ .alloc = ret };
|
||||
},
|
||||
'M' => {
|
||||
var tokens = std.mem.tokenize(u8, self.contents[1..], ",:");
|
||||
var tokens = std.mem.tokenizeAny(u8, self.contents[1..], ",:");
|
||||
|
||||
const addr_str = tokens.next() orelse return error.InvalidPacket;
|
||||
const length_str = tokens.next() orelse return error.InvalidPacket;
|
||||
@@ -122,7 +126,7 @@ pub fn parse(self: *Self, allocator: Allocator, state: *Server.State, emu: *Emul
|
||||
}
|
||||
},
|
||||
's' => {
|
||||
// var tokens = std.mem.tokenize(u8, self.contents[1..], " ");
|
||||
// var tokens = std.mem.tokenizeAny(u8, self.contents[1..], " ");
|
||||
// const addr = if (tokens.next()) |s| try std.fmt.parseInt(u32, s, 16) else null;
|
||||
|
||||
switch (emu.step()) {
|
||||
@@ -136,7 +140,7 @@ pub fn parse(self: *Self, allocator: Allocator, state: *Server.State, emu: *Emul
|
||||
|
||||
// Breakpoints
|
||||
'z' => {
|
||||
var tokens = std.mem.tokenize(u8, self.contents[2..], ",");
|
||||
var tokens = std.mem.tokenizeAny(u8, self.contents[2..], ",");
|
||||
|
||||
const addr_str = tokens.next() orelse return error.InvalidPacket;
|
||||
const addr = try std.fmt.parseInt(u32, addr_str, 16);
|
||||
@@ -157,7 +161,7 @@ pub fn parse(self: *Self, allocator: Allocator, state: *Server.State, emu: *Emul
|
||||
}
|
||||
},
|
||||
'Z' => {
|
||||
var tokens = std.mem.tokenize(u8, self.contents[2..], ",");
|
||||
var tokens = std.mem.tokenizeAny(u8, self.contents[2..], ",");
|
||||
const addr_str = tokens.next() orelse return error.InvalidPacket;
|
||||
const kind_str = tokens.next() orelse return error.InvalidPacket;
|
||||
|
||||
@@ -166,11 +170,11 @@ pub fn parse(self: *Self, allocator: Allocator, state: *Server.State, emu: *Emul
|
||||
|
||||
switch (self.contents[1]) {
|
||||
'0' => {
|
||||
try emu.addBkpt(.Software, addr, kind);
|
||||
try emu.addBkpt(.Software, allocator, addr, kind);
|
||||
return .{ .static = "OK" };
|
||||
},
|
||||
'1' => {
|
||||
emu.addBkpt(.Hardware, addr, kind) catch |e| {
|
||||
emu.addBkpt(.Hardware, allocator, addr, kind) catch |e| {
|
||||
switch (e) {
|
||||
error.OutOfSpace => return .{ .static = "E22" }, // FIXME: which errno?
|
||||
else => return e,
|
||||
@@ -232,7 +236,7 @@ pub fn parse(self: *Self, allocator: Allocator, state: *Server.State, emu: *Emul
|
||||
}
|
||||
|
||||
if (substr(self.contents[1..], "Xfer:features:read")) {
|
||||
var tokens = std.mem.tokenize(u8, self.contents[1..], ":,");
|
||||
var tokens = std.mem.tokenizeAny(u8, self.contents[1..], ":,");
|
||||
_ = tokens.next(); // Xfer
|
||||
_ = tokens.next(); // features
|
||||
_ = tokens.next(); // read
|
||||
@@ -266,7 +270,7 @@ pub fn parse(self: *Self, allocator: Allocator, state: *Server.State, emu: *Emul
|
||||
if (substr(self.contents[1..], "Xfer:memory-map:read")) {
|
||||
const mem_map = state.memmap_xml.?;
|
||||
|
||||
var tokens = std.mem.tokenize(u8, self.contents[1..], ":,");
|
||||
var tokens = std.mem.tokenizeAny(u8, self.contents[1..], ":,");
|
||||
_ = tokens.next(); // Xfer
|
||||
_ = tokens.next(); // memory-map
|
||||
_ = tokens.next(); // read
|
||||
|
||||
@@ -54,7 +54,7 @@ pub fn run(self: *Self, allocator: Allocator, should_quit: *std.atomic.Value(boo
|
||||
var buf: [Packet.max_len]u8 = undefined;
|
||||
|
||||
var client = try self.socket.accept();
|
||||
log.info("client connected from {}", .{client.address});
|
||||
log.info("client connected from {f}", .{client.address});
|
||||
|
||||
while (!should_quit.load(.monotonic)) {
|
||||
if (self.state.should_quit) {
|
||||
|
||||
@@ -1,31 +1,21 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
hw_bkpt: HwBkpt = .{},
|
||||
sw_bkpt: SwBkpt,
|
||||
sw_bkpt: SwBkpt = .{},
|
||||
|
||||
pub fn init(allocator: Allocator) @This() {
|
||||
return .{ .sw_bkpt = SwBkpt.init(allocator) };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
self.sw_bkpt.deinit();
|
||||
pub fn deinit(self: *@This(), allocator: Allocator) void {
|
||||
self.sw_bkpt.deinit(allocator);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
const SwBkpt = struct {
|
||||
const log = std.log.scoped(.SwBkpt);
|
||||
|
||||
list: std.ArrayList(Bkpt),
|
||||
list: std.ArrayList(Bkpt) = .empty,
|
||||
|
||||
pub fn init(allocator: Allocator) @This() {
|
||||
return .{ .list = ArrayList(Bkpt).init(allocator) };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
self.list.deinit();
|
||||
pub fn deinit(self: *@This(), allocator: Allocator) void {
|
||||
self.list.deinit(allocator);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
@@ -37,12 +27,12 @@ const SwBkpt = struct {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn add(self: *@This(), addr: u32, kind: u32) !void {
|
||||
pub fn add(self: *@This(), allocator: Allocator, addr: u32, kind: u32) !void {
|
||||
for (self.list.items) |bkpt| {
|
||||
if (bkpt.addr == addr) return; // indempotent
|
||||
}
|
||||
|
||||
try self.list.append(.{ .addr = addr, .kind = try Bkpt.Kind.from(u32, kind) });
|
||||
try self.list.append(allocator, .{ .addr = addr, .kind = try Bkpt.Kind.from(u32, kind) });
|
||||
log.warn("Added Breakpoint at 0x{X:0>8}", .{addr});
|
||||
}
|
||||
|
||||
@@ -110,7 +100,7 @@ const Bkpt = struct {
|
||||
Thumb = 4,
|
||||
|
||||
pub fn from(comptime T: type, num: T) !@This() {
|
||||
comptime std.debug.assert(@typeInfo(T) == .Int);
|
||||
comptime std.debug.assert(@typeInfo(T) == .int);
|
||||
|
||||
return switch (num) {
|
||||
2 => .Arm,
|
||||
|
||||
28
src/lib.zig
28
src/lib.zig
@@ -16,7 +16,7 @@ pub const Emulator = struct {
|
||||
SingleStep: void,
|
||||
};
|
||||
|
||||
state: State,
|
||||
state: State = .{},
|
||||
|
||||
ptr: *anyopaque,
|
||||
|
||||
@@ -28,42 +28,42 @@ pub const Emulator = struct {
|
||||
|
||||
stepFn: *const fn (*anyopaque) void,
|
||||
|
||||
pub fn init(allocator: Allocator, ptr: anytype) Self {
|
||||
pub fn init(ptr: anytype) Self {
|
||||
const Ptr = @TypeOf(ptr);
|
||||
const ptr_info = @typeInfo(Ptr);
|
||||
|
||||
if (ptr_info != .Pointer) @compileError("ptr must be a pointer");
|
||||
if (ptr_info.Pointer.size != .One) @compileError("ptr must be a single-item pointer");
|
||||
if (ptr_info != .pointer) @compileError("ptr must be a pointer");
|
||||
if (ptr_info.pointer.size != .one) @compileError("ptr must be a single-item pointer");
|
||||
|
||||
const gen = struct {
|
||||
pub fn readImpl(pointer: *anyopaque, addr: u32) u8 {
|
||||
const self: Ptr = @ptrCast(@alignCast(pointer));
|
||||
|
||||
return @call(.always_inline, ptr_info.Pointer.child.read, .{ self, addr });
|
||||
return @call(.always_inline, ptr_info.pointer.child.read, .{ self, addr });
|
||||
}
|
||||
|
||||
pub fn writeImpl(pointer: *anyopaque, addr: u32, value: u8) void {
|
||||
const self: Ptr = @ptrCast(@alignCast(pointer));
|
||||
|
||||
return @call(.always_inline, ptr_info.Pointer.child.write, .{ self, addr, value });
|
||||
return @call(.always_inline, ptr_info.pointer.child.write, .{ self, addr, value });
|
||||
}
|
||||
|
||||
pub fn registersImpl(pointer: *anyopaque) *[16]u32 {
|
||||
const self: Ptr = @ptrCast(@alignCast(pointer));
|
||||
|
||||
return @call(.always_inline, ptr_info.Pointer.child.registers, .{self});
|
||||
return @call(.always_inline, ptr_info.pointer.child.registers, .{self});
|
||||
}
|
||||
|
||||
pub fn cpsrImpl(pointer: *anyopaque) u32 {
|
||||
const self: Ptr = @ptrCast(@alignCast(pointer));
|
||||
|
||||
return @call(.always_inline, ptr_info.Pointer.child.cpsr, .{self});
|
||||
return @call(.always_inline, ptr_info.pointer.child.cpsr, .{self});
|
||||
}
|
||||
|
||||
pub fn stepImpl(pointer: *anyopaque) void {
|
||||
const self: Ptr = @ptrCast(@alignCast(pointer));
|
||||
|
||||
return @call(.always_inline, ptr_info.Pointer.child.step, .{self});
|
||||
return @call(.always_inline, ptr_info.pointer.child.step, .{self});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -74,13 +74,11 @@ pub const Emulator = struct {
|
||||
.registersFn = gen.registersImpl,
|
||||
.cpsrFn = gen.cpsrImpl,
|
||||
.stepFn = gen.stepImpl,
|
||||
|
||||
.state = State.init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.state.deinit();
|
||||
pub fn deinit(self: *Self, allocator: Allocator) void {
|
||||
self.state.deinit(allocator);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
@@ -128,10 +126,10 @@ pub const Emulator = struct {
|
||||
const BkptType = enum { Hardware, Software };
|
||||
|
||||
// TODO: Consider properly implementing Software interrupts?
|
||||
pub fn addBkpt(self: *Self, comptime @"type": BkptType, addr: u32, kind: u32) !void {
|
||||
pub fn addBkpt(self: *Self, comptime @"type": BkptType, allocator: Allocator, addr: u32, kind: u32) !void {
|
||||
switch (@"type") {
|
||||
.Hardware => try self.state.hw_bkpt.add(addr, kind),
|
||||
.Software => try self.state.sw_bkpt.add(addr, kind),
|
||||
.Software => try self.state.sw_bkpt.add(allocator, addr, kind),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
16
src/test.zig
16
src/test.zig
@@ -52,8 +52,8 @@ const BarebonesEmulator = struct {
|
||||
|
||||
r: [16]u32 = [_]u32{0} ** 16,
|
||||
|
||||
pub fn interface(self: *@This(), allocator: Allocator) Emulator {
|
||||
return Emulator.init(allocator, self);
|
||||
pub fn interface(self: *@This()) Emulator {
|
||||
return Emulator.init(self);
|
||||
}
|
||||
|
||||
pub fn read(_: *const @This(), _: u32) u8 {
|
||||
@@ -88,15 +88,19 @@ test Server {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
var impl = BarebonesEmulator{};
|
||||
var iface = impl.interface(allocator);
|
||||
defer iface.deinit();
|
||||
var iface = impl.interface();
|
||||
defer iface.deinit(allocator);
|
||||
|
||||
const clientFn = struct {
|
||||
fn inner(address: std.net.Address) !void {
|
||||
const socket = try std.net.tcpConnectToAddress(address);
|
||||
defer socket.close();
|
||||
|
||||
_ = try socket.writer().writeAll("+");
|
||||
var buf: [1024]u8 = undefined;
|
||||
|
||||
var writer = socket.writer(&buf).interface;
|
||||
|
||||
_ = try writer.writeAll("+");
|
||||
}
|
||||
}.inner;
|
||||
|
||||
@@ -142,7 +146,7 @@ test Emulator {
|
||||
};
|
||||
|
||||
var impl = ExampleImpl{};
|
||||
var emu = Emulator.init(std.testing.allocator, &impl);
|
||||
var emu = Emulator.init(&impl);
|
||||
|
||||
_ = emu.read(0x0000_0000);
|
||||
emu.write(0x0000_0000, 0x00);
|
||||
|
||||
Reference in New Issue
Block a user