Create LinkerscriptStep (#7)
* create linkerscript step * catch up to master, do some cleanup * remove references to micro_linker * commas * remove package * use root path trick for cpus and chips * remove duped file
This commit is contained in:
parent
af7aa777f9
commit
5a1e72f380
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
|
@ -9,8 +9,14 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [
|
||||
ubuntu-latest,
|
||||
windows-latest,
|
||||
macos-latest,
|
||||
]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
zig-cache/
|
||||
dev-scripts/
|
||||
dev-scripts/
|
||||
zig-out
|
||||
|
|
139
build.zig
139
build.zig
|
@ -3,6 +3,13 @@
|
|||
//! This means we need to use addExecutable() instead of using
|
||||
|
||||
const std = @import("std");
|
||||
const boards = @import("src/modules/boards.zig");
|
||||
const chips = @import("src/modules/chips.zig");
|
||||
const LinkerscriptStep = @import("src/modules/LinkerScriptStep.zig");
|
||||
|
||||
const Board = boards.Board;
|
||||
const Chip = chips.Chip;
|
||||
const Cpu = chips.Cpu;
|
||||
|
||||
pub fn build(b: *std.build.Builder) !void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
@ -12,9 +19,9 @@ pub fn build(b: *std.build.Builder) !void {
|
|||
const BuildConfig = struct { name: []const u8, backing: Backing };
|
||||
const all_backings = [_]BuildConfig{
|
||||
//BuildConfig{ .name = "boards.arduino_nano", .backing = Backing{ .board = pkgs.boards.arduino_nano } },
|
||||
BuildConfig{ .name = "boards.mbed_lpc1768", .backing = Backing{ .board = pkgs.boards.mbed_lpc1768 } },
|
||||
BuildConfig{ .name = "boards.mbed_lpc1768", .backing = Backing{ .board = boards.mbed_lpc1768 } },
|
||||
//BuildConfig{ .name = "chips.atmega328p", .backing = Backing{ .chip = pkgs.chips.atmega328p } },
|
||||
//BuildConfig{ .name = "chips.lpc1768", .backing = Backing{ .chip = pkgs.chips.lpc1768 } },
|
||||
BuildConfig{ .name = "chips.lpc1768", .backing = Backing{ .chip = chips.lpc1768 } },
|
||||
};
|
||||
|
||||
const Test = struct { name: []const u8, source: []const u8 };
|
||||
|
@ -45,7 +52,12 @@ pub fn build(b: *std.build.Builder) !void {
|
|||
}
|
||||
}
|
||||
|
||||
fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source: []const u8, backing: Backing) !*std.build.LibExeObjStep {
|
||||
fn addEmbeddedExecutable(
|
||||
builder: *std.build.Builder,
|
||||
name: []const u8,
|
||||
source: []const u8,
|
||||
backing: Backing,
|
||||
) !*std.build.LibExeObjStep {
|
||||
const Pkg = std.build.Pkg;
|
||||
|
||||
const microzig_base = Pkg{
|
||||
|
@ -71,40 +83,9 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source:
|
|||
.path = .{ .path = chip.cpu.path },
|
||||
.dependencies = &[_]Pkg{ microzig_base, pkgs.mmio },
|
||||
},
|
||||
Pkg{
|
||||
.name = "microzig-linker",
|
||||
.path = .{ .path = "src/modules/linker/linker.zig" },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const linker_script_name = blk: {
|
||||
const hash = hash_blk: {
|
||||
var hasher = std.hash.SipHash128(1, 2).init("abcdefhijklmnopq");
|
||||
|
||||
hasher.update(chip.name);
|
||||
hasher.update(chip.path);
|
||||
hasher.update(chip.cpu.name);
|
||||
hasher.update(chip.cpu.path);
|
||||
|
||||
var mac: [16]u8 = undefined;
|
||||
hasher.final(&mac);
|
||||
break :hash_blk mac;
|
||||
};
|
||||
|
||||
const file_prefix = "zig-cache/microzig/";
|
||||
const file_suffix = ".ld";
|
||||
|
||||
var ld_file_name: [file_prefix.len + 2 * hash.len + file_suffix.len]u8 = undefined;
|
||||
const filename = try std.fmt.bufPrint(&ld_file_name, "{s}{}{s}", .{
|
||||
file_prefix,
|
||||
std.fmt.fmtSliceHexLower(&hash),
|
||||
file_suffix,
|
||||
});
|
||||
|
||||
break :blk builder.dupe(filename);
|
||||
};
|
||||
|
||||
const config_file_name = blk: {
|
||||
const hash = hash_blk: {
|
||||
var hasher = std.hash.SipHash128(1, 2).init("abcdefhijklmnopq");
|
||||
|
@ -156,22 +137,7 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source:
|
|||
.path = .{ .path = config_file_name },
|
||||
};
|
||||
|
||||
const build_options = builder.addOptions();
|
||||
build_options.addOption([]const u8, "microzig_chip_name", chip.name);
|
||||
build_options.addOption([]const u8, "microzig_cpu_name", chip.cpu.name);
|
||||
build_options.addOption([]const u8, "microzig_target_triple", try chip.cpu.target.zigTriple(builder.allocator));
|
||||
|
||||
const linkerscript_gen = builder.addExecutable("linkerscript-gen", "src/tools/linkerscript-gen.zig");
|
||||
linkerscript_gen.addPackage(chip_package);
|
||||
linkerscript_gen.addPackage(Pkg{
|
||||
.name = "microzig-linker",
|
||||
.path = .{ .path = "src/modules/linker/linker.zig" },
|
||||
});
|
||||
linkerscript_gen.addOptions("build_options", build_options);
|
||||
|
||||
const linkerscript_invocation = linkerscript_gen.run();
|
||||
linkerscript_invocation.addArg(linker_script_name);
|
||||
|
||||
const linkerscript = try LinkerscriptStep.create(builder, chip);
|
||||
const exe = builder.addExecutable(name, source);
|
||||
|
||||
// might not be true for all machines (Pi Pico), but
|
||||
|
@ -179,8 +145,7 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source:
|
|||
exe.single_threaded = true;
|
||||
exe.setTarget(chip.cpu.target);
|
||||
|
||||
exe.setLinkerScriptPath(.{ .path = linker_script_name });
|
||||
exe.step.dependOn(&linkerscript_invocation.step);
|
||||
exe.setLinkerScriptPath(.{ .generated = &linkerscript.generated_file });
|
||||
|
||||
// TODO:
|
||||
// - Generate the linker scripts from the "chip" or "board" package instead of using hardcoded ones.
|
||||
|
@ -216,25 +181,6 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source:
|
|||
return exe;
|
||||
}
|
||||
|
||||
const Board = struct {
|
||||
name: []const u8,
|
||||
path: []const u8,
|
||||
chip: Chip,
|
||||
};
|
||||
|
||||
const Chip = struct {
|
||||
name: []const u8,
|
||||
path: []const u8,
|
||||
cpu: Cpu,
|
||||
};
|
||||
|
||||
const Cpu = struct {
|
||||
name: []const u8,
|
||||
path: []const u8,
|
||||
target: std.zig.CrossTarget,
|
||||
linker_script: []const u8,
|
||||
};
|
||||
|
||||
pub const Backing = union(enum) {
|
||||
board: Board,
|
||||
chip: Chip,
|
||||
|
@ -245,55 +191,4 @@ const pkgs = struct {
|
|||
.name = "microzig-mmio",
|
||||
.path = .{ .path = "src/core/mmio.zig" },
|
||||
};
|
||||
|
||||
const cpus = struct {
|
||||
const avr5 = Cpu{
|
||||
.name = "AVR5",
|
||||
.path = "src/modules/cpus/avr/avr5.zig",
|
||||
.linker_script = "src/modules/cpus/avr/linker.ld",
|
||||
.target = std.zig.CrossTarget{
|
||||
.cpu_arch = .avr,
|
||||
.cpu_model = .{ .explicit = &std.Target.avr.cpu.avr5 },
|
||||
.os_tag = .freestanding,
|
||||
.abi = .eabi,
|
||||
},
|
||||
};
|
||||
const cortex_m3 = Cpu{
|
||||
.name = "ARM Cortex-M3",
|
||||
.path = "src/modules/cpus/cortex-m3/cortex-m3.zig",
|
||||
.linker_script = "src/modules/cpus/cortex-m3/linker.ld",
|
||||
.target = std.zig.CrossTarget{
|
||||
.cpu_arch = .arm,
|
||||
.cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m3 },
|
||||
.os_tag = .freestanding,
|
||||
.abi = .none,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const chips = struct {
|
||||
const atmega328p = Chip{
|
||||
.name = "ATmega328p",
|
||||
.path = "src/modules/chips/atmega328p/atmega328p.zig",
|
||||
.cpu = cpus.avr5,
|
||||
};
|
||||
const lpc1768 = Chip{
|
||||
.name = "NXP LPC1768",
|
||||
.path = "src/modules/chips/lpc1768/lpc1768.zig",
|
||||
.cpu = cpus.cortex_m3,
|
||||
};
|
||||
};
|
||||
|
||||
const boards = struct {
|
||||
const arduino_nano = Board{
|
||||
.name = "Arduino Nano",
|
||||
.path = "src/modules/boards/arduino-nano/arduino-nano.zig",
|
||||
.chip = chips.atmega328p,
|
||||
};
|
||||
const mbed_lpc1768 = Board{
|
||||
.name = "mbed LPC1768",
|
||||
.path = "src/modules/boards/mbed-lpc1768/mbed-lpc1768.zig",
|
||||
.chip = chips.lpc1768,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -27,7 +27,9 @@ fn writerWrite(ctx: void, string: []const u8) DebugErr!usize {
|
|||
const DebugWriter = std.io.Writer(void, DebugErr, writerWrite);
|
||||
|
||||
pub fn write(string: []const u8) void {
|
||||
if (!micro.config.has_board and !@hasDecl(micro.board, "debugWrite"))
|
||||
if (!micro.config.has_board)
|
||||
return;
|
||||
if (!@hasDecl(micro.board, "debugWrite"))
|
||||
return;
|
||||
|
||||
micro.board.debugWrite(string);
|
||||
|
|
|
@ -1,46 +1,72 @@
|
|||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
const chip = @import("chip");
|
||||
const micro_linker = @import("microzig-linker");
|
||||
const MemoryRegion = @import("MemoryRegion.zig");
|
||||
const Chip = @import("chips.zig").Chip;
|
||||
const Step = std.build.Step;
|
||||
const Builder = std.build.Builder;
|
||||
const GeneratedFile = std.build.GeneratedFile;
|
||||
|
||||
const target = blk: {
|
||||
@setEvalBranchQuota(20_000);
|
||||
const LinkerscriptStep = @This();
|
||||
|
||||
const t = std.zig.CrossTarget.parse(std.zig.CrossTarget.ParseOptions{
|
||||
.arch_os_abi = build_options.microzig_target_triple,
|
||||
}) catch unreachable;
|
||||
std.debug.assert(t.cpu_arch != null);
|
||||
break :blk t;
|
||||
};
|
||||
step: Step,
|
||||
generated_file: std.build.GeneratedFile,
|
||||
builder: *Builder,
|
||||
chip: Chip,
|
||||
|
||||
pub fn main() !u8 {
|
||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer arena.deinit();
|
||||
pub fn create(builder: *Builder, chip: Chip) !*LinkerscriptStep {
|
||||
var hasher = std.hash.SipHash128(1, 2).init("abcdefhijklmnopq");
|
||||
|
||||
const allocator = &arena.allocator;
|
||||
hasher.update(chip.name);
|
||||
hasher.update(chip.path);
|
||||
hasher.update(chip.cpu.name);
|
||||
hasher.update(chip.cpu.path);
|
||||
|
||||
const args = try std.process.argsAlloc(allocator);
|
||||
var mac: [16]u8 = undefined;
|
||||
hasher.final(&mac);
|
||||
|
||||
if (args.len < 2) {
|
||||
std.log.err("Missing CLI argument. Give the output file name!", .{});
|
||||
return 1;
|
||||
const filename = try std.fmt.allocPrint(builder.allocator, "{}{s}", .{
|
||||
std.fmt.fmtSliceHexLower(&mac),
|
||||
".ld",
|
||||
});
|
||||
|
||||
const path = try std.fs.path.join(builder.allocator, &.{
|
||||
"zig-cache",
|
||||
"microzig",
|
||||
filename,
|
||||
});
|
||||
try std.fs.cwd().makePath(std.fs.path.dirname(path).?);
|
||||
|
||||
var ret = try builder.allocator.create(LinkerscriptStep);
|
||||
ret.* = LinkerscriptStep{
|
||||
.step = Step.init(.custom, "linkerscript", builder.allocator, make),
|
||||
.generated_file = .{
|
||||
.step = &ret.step,
|
||||
.path = path,
|
||||
},
|
||||
.builder = builder,
|
||||
.chip = chip,
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn make(step: *Step) !void {
|
||||
const linkerscript = @fieldParentPtr(LinkerscriptStep, "step", step);
|
||||
const file = try std.fs.cwd().createFile(linkerscript.generated_file.path.?, .{});
|
||||
defer file.close();
|
||||
|
||||
const target = linkerscript.chip.cpu.target;
|
||||
if (target.cpu_arch == null) {
|
||||
std.log.err("target does not have 'cpu_arch'", .{});
|
||||
return error.NoCpuArch;
|
||||
}
|
||||
|
||||
if (std.fs.path.dirname(args[1])) |dir| {
|
||||
try std.fs.cwd().makePath(dir);
|
||||
}
|
||||
|
||||
var dest_file = try std.fs.cwd().createFile(args[1], .{});
|
||||
defer dest_file.close();
|
||||
|
||||
var writer = dest_file.writer();
|
||||
|
||||
try writer.writeAll("/*\n * This file was auto-generated by microzig\n *\n");
|
||||
try writer.print(" * Target CPU: {s}\n", .{build_options.microzig_cpu_name});
|
||||
try writer.print(" * Target Chip: {s}\n", .{build_options.microzig_chip_name});
|
||||
try writer.writeAll(" */\n\n");
|
||||
|
||||
try writer.writeAll(
|
||||
const writer = file.writer();
|
||||
try writer.print(
|
||||
\\/*
|
||||
\\ * This file was auto-generated by microzig
|
||||
\\ *
|
||||
\\ * Target CPU: {s}
|
||||
\\ * Target Chip: {s}
|
||||
\\ */
|
||||
\\
|
||||
// This is not the "true" entry point, but there's no such thing on embedded platforms
|
||||
// anyways. This is the logical entrypoint that should be invoked when
|
||||
|
@ -48,12 +74,12 @@ pub fn main() !u8 {
|
|||
\\ENTRY(microzig_main);
|
||||
\\
|
||||
\\
|
||||
);
|
||||
, .{ linkerscript.chip.cpu.name, linkerscript.chip.name });
|
||||
|
||||
try writer.writeAll("MEMORY\n{\n");
|
||||
{
|
||||
var counters = [2]usize{ 0, 0 };
|
||||
for (chip.memory_regions) |region| {
|
||||
for (linkerscript.chip.memory_regions) |region| {
|
||||
// flash (rx!w) : ORIGIN = 0x00000000, LENGTH = 512k
|
||||
|
||||
switch (region.kind) {
|
||||
|
@ -134,5 +160,4 @@ pub fn main() !u8 {
|
|||
// \\ ASSERT( (SIZEOF(.text) + SIZEOF(.data) > LENGTH(flash0)), "Error: .text + .data is too large for flash!" );
|
||||
// \\
|
||||
// );
|
||||
return 0;
|
||||
}
|
18
src/modules/MemoryRegion.zig
Normal file
18
src/modules/MemoryRegion.zig
Normal file
|
@ -0,0 +1,18 @@
|
|||
//! This module is meant to be used to define linking apis
|
||||
|
||||
kind: Kind,
|
||||
offset: u64,
|
||||
length: u64,
|
||||
|
||||
pub const Kind = union(enum) {
|
||||
flash,
|
||||
ram,
|
||||
custom: RegionSpec,
|
||||
};
|
||||
|
||||
pub const RegionSpec = struct {
|
||||
name: []const u8,
|
||||
executable: bool,
|
||||
readable: bool,
|
||||
writeable: bool,
|
||||
};
|
20
src/modules/boards.zig
Normal file
20
src/modules/boards.zig
Normal file
|
@ -0,0 +1,20 @@
|
|||
const chips = @import("chips.zig");
|
||||
|
||||
const Chip = chips.Chip;
|
||||
pub const Board = struct {
|
||||
name: []const u8,
|
||||
path: []const u8,
|
||||
chip: Chip,
|
||||
};
|
||||
|
||||
pub const arduino_nano = Board{
|
||||
.name = "Arduino Nano",
|
||||
.path = "src/modules/boards/arduino-nano/arduino-nano.zig",
|
||||
.chip = chips.atmega328p,
|
||||
};
|
||||
|
||||
pub const mbed_lpc1768 = Board{
|
||||
.name = "mbed LPC1768",
|
||||
.path = "src/modules/boards/mbed-lpc1768/mbed-lpc1768.zig",
|
||||
.chip = chips.lpc1768,
|
||||
};
|
37
src/modules/chips.zig
Normal file
37
src/modules/chips.zig
Normal file
|
@ -0,0 +1,37 @@
|
|||
const std = @import("std");
|
||||
const cpus = @import("cpus.zig");
|
||||
const MemoryRegion = @import("MemoryRegion.zig");
|
||||
|
||||
fn root() []const u8 {
|
||||
return std.fs.path.dirname(@src().file) orelse unreachable;
|
||||
}
|
||||
|
||||
const root_path = root() ++ "/";
|
||||
|
||||
pub const Chip = struct {
|
||||
name: []const u8,
|
||||
path: []const u8,
|
||||
cpu: cpus.Cpu,
|
||||
memory_regions: []const MemoryRegion,
|
||||
};
|
||||
|
||||
pub const atmega328p = Chip{
|
||||
.name = "ATmega328p",
|
||||
.path = root_path ++ "chips/atmega328p/atmega328p.zig",
|
||||
.cpu = cpus.avr5,
|
||||
.memory_regions = &[_]MemoryRegion{
|
||||
MemoryRegion{ .offset = 0x000000, .length = 32 * 1024, .kind = .flash },
|
||||
MemoryRegion{ .offset = 0x800100, .length = 2048, .kind = .ram },
|
||||
},
|
||||
};
|
||||
|
||||
pub const lpc1768 = Chip{
|
||||
.name = "NXP LPC1768",
|
||||
.path = root_path ++ "chips/lpc1768/lpc1768.zig",
|
||||
.cpu = cpus.cortex_m3,
|
||||
.memory_regions = &[_]MemoryRegion{
|
||||
MemoryRegion{ .offset = 0x00000000, .length = 512 * 1024, .kind = .flash },
|
||||
MemoryRegion{ .offset = 0x10000000, .length = 32 * 1024, .kind = .ram },
|
||||
MemoryRegion{ .offset = 0x2007C000, .length = 32 * 1024, .kind = .ram },
|
||||
},
|
||||
};
|
|
@ -1,13 +1,6 @@
|
|||
const std = @import("std");
|
||||
const micro_linker = @import("microzig-linker");
|
||||
|
||||
pub const cpu = @import("cpu");
|
||||
|
||||
pub const memory_regions = [_]micro_linker.MemoryRegion{
|
||||
micro_linker.MemoryRegion{ .offset = 0x000000, .length = 32 * 1024, .kind = .flash },
|
||||
micro_linker.MemoryRegion{ .offset = 0x800100, .length = 2048, .kind = .ram },
|
||||
};
|
||||
|
||||
const Port = enum(u8) {
|
||||
B = 1,
|
||||
C = 2,
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
const std = @import("std");
|
||||
const micro = @import("microzig");
|
||||
const micro_linker = @import("microzig-linker");
|
||||
|
||||
pub const cpu = @import("cpu");
|
||||
pub const registers = @import("registers.zig");
|
||||
|
||||
pub const memory_regions = [_]micro_linker.MemoryRegion{
|
||||
micro_linker.MemoryRegion{ .offset = 0x00000000, .length = 512 * 1024, .kind = .flash },
|
||||
micro_linker.MemoryRegion{ .offset = 0x10000000, .length = 32 * 1024, .kind = .ram },
|
||||
micro_linker.MemoryRegion{ .offset = 0x2007C000, .length = 32 * 1024, .kind = .ram },
|
||||
};
|
||||
|
||||
pub const PinTarget = enum(u2) {
|
||||
func00 = 0b00,
|
||||
func01 = 0b01,
|
||||
|
|
35
src/modules/cpus.zig
Normal file
35
src/modules/cpus.zig
Normal file
|
@ -0,0 +1,35 @@
|
|||
const std = @import("std");
|
||||
|
||||
fn root() []const u8 {
|
||||
return std.fs.path.dirname(@src().file) orelse unreachable;
|
||||
}
|
||||
|
||||
const root_path = root() ++ "/";
|
||||
|
||||
pub const Cpu = struct {
|
||||
name: []const u8,
|
||||
path: []const u8,
|
||||
target: std.zig.CrossTarget,
|
||||
};
|
||||
|
||||
pub const avr5 = Cpu{
|
||||
.name = "AVR5",
|
||||
.path = root_path ++ "cpus/avr/avr5.zig",
|
||||
.target = std.zig.CrossTarget{
|
||||
.cpu_arch = .avr,
|
||||
.cpu_model = .{ .explicit = &std.Target.avr.cpu.avr5 },
|
||||
.os_tag = .freestanding,
|
||||
.abi = .eabi,
|
||||
},
|
||||
};
|
||||
|
||||
pub const cortex_m3 = Cpu{
|
||||
.name = "ARM Cortex-M3",
|
||||
.path = root_path ++ "cpus/cortex-m3/cortex-m3.zig",
|
||||
.target = std.zig.CrossTarget{
|
||||
.cpu_arch = .arm,
|
||||
.cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m3 },
|
||||
.os_tag = .freestanding,
|
||||
.abi = .none,
|
||||
},
|
||||
};
|
|
@ -71,11 +71,11 @@ pub const startup_logic = struct {
|
|||
|
||||
extern fn microzig_main() noreturn;
|
||||
|
||||
extern var microzig_data_start: c_void;
|
||||
extern var microzig_data_end: c_void;
|
||||
extern var microzig_bss_start: c_void;
|
||||
extern var microzig_bss_end: c_void;
|
||||
extern const microzig_data_load_start: c_void;
|
||||
extern var microzig_data_start: anyopaque;
|
||||
extern var microzig_data_end: anyopaque;
|
||||
extern var microzig_bss_start: anyopaque;
|
||||
extern var microzig_bss_end: anyopaque;
|
||||
extern const microzig_data_load_start: anyopaque;
|
||||
|
||||
fn _start() callconv(.C) noreturn {
|
||||
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
//! This module is meant to be used to define linking apis
|
||||
|
||||
pub const MemoryRegion = struct {
|
||||
kind: Kind,
|
||||
offset: u64,
|
||||
length: u64,
|
||||
|
||||
pub const Kind = union(enum) {
|
||||
flash,
|
||||
ram,
|
||||
custom: RegionSpec,
|
||||
};
|
||||
|
||||
pub const RegionSpec = struct {
|
||||
name: []const u8,
|
||||
executable: bool,
|
||||
readable: bool,
|
||||
writeable: bool,
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue
Block a user