-
-
Save daurnimator/6518ece625b9c5f143ac51274b9dacfe to your computer and use it in GitHub Desktop.
Trying to write a linux kernel module in zig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const Builder = std.build.Builder; | |
const Step = std.build.Step; | |
fn uname() !std.os.utsname { | |
var uts: std.os.utsname = undefined; | |
return switch (std.os.errno(std.os.linux.uname(&uts))) { | |
0 => uts, | |
std.os.EFAULT => unreachable, | |
std.os.EPERM => error.PermissionDenied, | |
else => |err| std.os.unexpectedErrno(err), | |
}; | |
} | |
var current_uname: ?std.os.utsname = null; | |
fn getUname() !*std.os.utsname { | |
if (current_uname == null) { | |
current_uname = try uname(); | |
} | |
return ¤t_uname.?; | |
} | |
fn getKernelRelease() ![]const u8 { | |
const uts = try getUname(); | |
return std.mem.toSliceConst(u8, @ptrCast([*:0]const u8, &uts.release)); | |
} | |
pub fn build(b: *Builder) !void { | |
const target = b.standardTargetOptions(.{ | |
.default_target = .{ | |
.cpu_model = .baseline, | |
.os_tag = .freestanding, | |
.abi = .gnu, | |
}, | |
}); | |
const mode = b.standardReleaseOptions(); | |
const module = b.addObject("my_module", "my_module.zig"); | |
module.setTarget(target); | |
module.setBuildMode(mode); | |
module.setDisableGenH(true); | |
const kernel_version = b.option([]const u8, "kernel-version", "kernel version") orelse try getKernelRelease(); | |
const dir = try std.mem.concat(b.allocator, u8, &[_][]const u8{ "/lib/modules/", kernel_version, "/build/" }); | |
defer b.allocator.free(dir); | |
const arch = "x86"; // TODO | |
const arch_include_dir = try std.mem.concat(b.allocator, u8, &[_][]const u8{ dir, "arch/", arch, "/include" }); | |
defer b.allocator.free(arch_include_dir); | |
module.addIncludeDir(arch_include_dir); | |
module.addIncludeDir(try std.mem.concat(b.allocator, u8, &[_][]const u8{ arch_include_dir, "/generated" })); | |
module.addIncludeDir(try std.mem.concat(b.allocator, u8, &[_][]const u8{ dir, "include" })); | |
module.addIncludeDir(try std.mem.concat(b.allocator, u8, &[_][]const u8{ arch_include_dir, "/uapi" })); | |
module.addIncludeDir(try std.mem.concat(b.allocator, u8, &[_][]const u8{ arch_include_dir, "/generated/uapi" })); | |
module.addIncludeDir(try std.mem.concat(b.allocator, u8, &[_][]const u8{ dir, "include/uapi" })); | |
module.addIncludeDir(try std.mem.concat(b.allocator, u8, &[_][]const u8{ dir, "include/generated/uapi" })); | |
module.setOutputDir("."); | |
const orc = b.addSystemCommand(&[_][]const u8{ | |
try std.mem.concat(b.allocator, u8, &[_][]const u8{ dir, "tools/objtool/objtool" }), | |
"orc", | |
"generate", | |
"--module", | |
"--no-fp", | |
"--retpoline", | |
"--uaccess", | |
}); | |
orc.addArtifactArg(module); | |
b.default_step.dependOn(&orc.step); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
// TODO: Have this function do the export | |
// https://github.com/ziglang/zig/issues/3753 | |
fn MODULE_INFO(comptime tag: []const u8, comptime info: []const u8) [tag.len + 1 + info.len:0]u8 { | |
return (tag ++ "=" ++ info ++ "").*; | |
} | |
// The names of these symbols doesn't matter | |
export const MODINFO_0 linksection(".modinfo") = MODULE_INFO("name", "my_module"); | |
export const MODINFO_1 linksection(".modinfo") = MODULE_INFO("license", "Dual MIT/GPL"); | |
export const MODINFO_2 linksection(".modinfo") = MODULE_INFO("author", "Daurnimator"); | |
export const MODINFO_3 linksection(".modinfo") = MODULE_INFO("description", "A proof of concept kernel module written in zig"); | |
export const MODINFO_4 linksection(".modinfo") = MODULE_INFO("version", "0.0"); | |
// TODO: https://github.com/ziglang/zig/issues/3755 | |
// export const MODINFO_5 linksection(".modinfo") = MODULE_INFO("retpoline", "Y"); | |
const linux = @cImport({ | |
@cDefine("__KERNEL__", {}); | |
@cDefine("MODULE", {}); | |
@cDefine("KBUILD_BASENAME", "\"my_module\""); | |
@cDefine("KBUILD_MODNAME", "\"my_module\""); | |
@cInclude("linux/kconfig.h"); | |
// Workaround recommended by Kees Cook (similar to hack done in bpf headers) | |
@cInclude("linux/types.h"); | |
@cUndef("asm_inline"); | |
@cDefine("asm_inline", "asm"); | |
@cInclude("linux/init.h"); | |
@cInclude("linux/module.h"); | |
@cInclude("linux/kernel.h"); | |
@cInclude("linux/printk.h"); | |
}); | |
// TODO: https://github.com/ziglang/zig/issues/1499 | |
// As a workaround I fake everything that uses `kobject` | |
const fake_module = struct { | |
usingnamespace linux; | |
pub const fake_struct_kobject = extern struct { | |
name: [*:0]const u8, | |
entry: struct_list_head, | |
parent: *fake_struct_kobject, | |
// kset: [*c]struct_kset, | |
kset: *@OpaqueType(), | |
ktype: [*c]struct_kobj_type, | |
sd: [*c]struct_kernfs_node, | |
kref: struct_kref, | |
// unsigned int state_initialized:1; | |
// unsigned int state_in_sysfs:1; | |
// unsigned int state_add_uevent_sent:1; | |
// unsigned int state_remove_uevent_sent:1; | |
// unsigned int uevent_suppress:1; | |
packed_bits: u8, | |
}; | |
pub const fake_struct_module_kobject = extern struct { | |
kobj: fake_struct_kobject, | |
mod: [*c]fake_struct_module, | |
drivers_dir: ?*fake_struct_kobject, | |
mp: ?*struct_module_param_attrs, | |
kobj_completion: [*c]struct_completion, | |
}; | |
pub const fake_struct_mod_tree_node = extern struct { | |
mod: [*c]fake_struct_module, | |
node: struct_latch_tree_node, | |
}; | |
pub const fake_struct_module_layout = extern struct { | |
base: ?*c_void, | |
size: c_uint, | |
text_size: c_uint, | |
ro_size: c_uint, | |
ro_after_init_size: c_uint, | |
mtn: fake_struct_mod_tree_node, | |
}; | |
pub const fake_struct_module = extern struct { | |
state: enum_module_state, | |
list: struct_list_head, | |
name: [56]u8, | |
mkobj: fake_struct_module_kobject, | |
modinfo_attrs: *@OpaqueType(), | |
version: [*c]const u8, | |
srcversion: [*c]const u8, | |
holders_dir: ?*fake_struct_kobject, | |
syms: [*c]const struct_kernel_symbol, | |
crcs: [*c]const s32, | |
num_syms: c_uint, | |
param_lock: struct_mutex, | |
kp: *@OpaqueType(), | |
num_kp: c_uint, | |
num_gpl_syms: c_uint, | |
gpl_syms: [*c]const struct_kernel_symbol, | |
gpl_crcs: [*c]const s32, | |
unused_syms: [*c]const struct_kernel_symbol, | |
unused_crcs: [*c]const s32, | |
num_unused_syms: c_uint, | |
num_unused_gpl_syms: c_uint, | |
unused_gpl_syms: [*c]const struct_kernel_symbol, | |
unused_gpl_crcs: [*c]const s32, | |
sig_ok: _bool, | |
async_probe_requested: _bool, | |
gpl_future_syms: [*c]const struct_kernel_symbol, | |
gpl_future_crcs: [*c]const s32, | |
num_gpl_future_syms: c_uint, | |
num_exentries: c_uint, | |
extable: ?*struct_exception_table_entry, | |
init: ?fn () callconv(.C) c_int, | |
core_layout: fake_struct_module_layout, | |
init_layout: fake_struct_module_layout, | |
arch: struct_mod_arch_specific, | |
taints: c_ulong, | |
num_bugs: c_uint, | |
bug_list: struct_list_head, | |
bug_table: [*c]struct_bug_entry, | |
kallsyms: [*c]struct_mod_kallsyms, | |
core_kallsyms: struct_mod_kallsyms, | |
sect_attrs: ?*struct_module_sect_attrs, | |
notes_attrs: ?*struct_module_notes_attrs, | |
args: [*c]u8, | |
percpu: ?*c_void, | |
percpu_size: c_uint, | |
num_tracepoints: c_uint, | |
tracepoints_ptrs: [*c]const tracepoint_ptr_t, | |
num_srcu_structs: c_uint, | |
srcu_struct_ptrs: [*c][*c]struct_srcu_struct, | |
num_bpf_raw_events: c_uint, | |
bpf_raw_events: [*c]struct_bpf_raw_event_map, | |
jump_entries: [*c]struct_jump_entry, | |
num_jump_entries: c_uint, | |
num_trace_bprintk_fmt: c_uint, | |
trace_bprintk_fmt_start: [*c][*c]const u8, | |
trace_events: [*c]?*struct_trace_event_call, | |
num_trace_events: c_uint, | |
trace_evals: [*c]?*struct_trace_eval_map, | |
num_trace_evals: c_uint, | |
num_ftrace_callsites: c_uint, | |
ftrace_callsites: [*c]c_ulong, | |
source_list: struct_list_head, | |
target_list: struct_list_head, | |
exit: ?fn () callconv(.C) void, | |
refcnt: atomic_t, | |
ei_funcs: [*c]struct_error_injection_entry, | |
num_ei_funcs: c_uint, | |
}; | |
}.fake_struct_module; | |
export var __this_module linksection(".gnu.linkonce.this_module") = blk: { | |
// var module: linux.module = undefined; | |
var module: fake_module = undefined; | |
module.name = ("my_module" ++ [_]u8{0} ** 47).*; | |
module.init = init_module; | |
module.exit = cleanup_module; | |
break :blk module; | |
}; | |
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | |
@setCold(true); | |
// TODO: https://github.com/ziglang/zig/issues/1481#issuecomment-557872850 | |
// linux.panic("%s", message); | |
linux.panic("zig panic!"); | |
} | |
export fn init_module() linksection(".init.text") c_int { | |
@setCold(true); | |
_ = linux.printk("hello\n"); | |
return 0; | |
} | |
export fn cleanup_module() linksection(".exit.text") void { | |
@setCold(true); | |
_ = linux.printk("bye\n"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment