diff --git a/build.zig b/build.zig index bc3ee1f..9dca029 100644 --- a/build.zig +++ b/build.zig @@ -2,7 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); const fs = std.fs; -fn create_bpf_prog(ctx: *const Ctx, src_path: []const u8) *std.Build.Step.Compile { +fn create_bpf_prog(ctx: *const Ctx, src_path: []const u8) std.Build.LazyPath { const name = fs.path.stem(src_path); const prog = ctx.b.addObject(.{ @@ -22,23 +22,19 @@ fn create_bpf_prog(ctx: *const Ctx, src_path: []const u8) *std.Build.Step.Compil prog.root_module.addImport("build_options", ctx.bpf); prog.root_module.addOptions("build_options", ctx.build_options); - return prog; + const run_btf_sanitizer = ctx.b.addRunArtifact(ctx.btf_sanitizer); + run_btf_sanitizer.addFileArg(prog.getEmittedBin()); + return run_btf_sanitizer.addOutputFileArg(ctx.b.fmt("{s}_sanitized.o", .{name})); } fn create_libbpf(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { return b.dependency("libbpf", .{ .target = target, .optimize = optimize, - .zig_wa = true, }).artifact("bpf"); } -fn create_vmlinux(b: *std.Build) *std.Build.Module { - // build for native - const target = b.host; - const optimize: std.builtin.OptimizeMode = .ReleaseFast; - - const libbpf = create_libbpf(b, target, optimize); +fn create_vmlinux(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, libbpf: *std.Build.Step.Compile) *std.Build.Module { const exe = b.addExecutable(.{ .name = "vmlinux_dumper", .root_source_file = b.path("src/vmlinux_dumper/main.zig"), @@ -64,6 +60,38 @@ fn create_vmlinux(b: *std.Build) *std.Build.Module { return b.addModule("vmlinux", .{ .root_source_file = .{ .generated = .{ .file = &zigify.output_file } } }); } +fn create_btf_sanitizer(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, libbpf: *std.Build.Step.Compile) *std.Build.Step.Compile { + const exe = b.addExecutable(.{ + .name = "btf_sanitizer", + .root_source_file = b.path("src/btf_sanitizer/main.zig"), + .target = target, + .optimize = optimize, + }); + const libelf = b.dependency("libelf", .{ + .target = target, + .optimize = optimize, + }).artifact("elf"); + exe.linkLibrary(libelf); + exe.linkLibrary(libbpf); + exe.linkLibC(); + b.installArtifact(exe); + + return exe; +} + +fn create_native_tools(b: *std.Build) struct { *std.Build.Module, *std.Build.Step.Compile } { + // build for native + const target = b.host; + const optimize: std.builtin.OptimizeMode = .ReleaseFast; + + const libbpf = create_libbpf(b, target, optimize); + + return .{ + create_vmlinux(b, target, optimize, libbpf), + create_btf_sanitizer(b, target, optimize, libbpf), + }; +} + fn create_bpf(b: *std.Build, vmlinux: *std.Build.Module) *std.Build.Module { return b.addModule("bpf", .{ .root_source_file = b.path("src/bpf/root.zig"), @@ -79,6 +107,7 @@ const Ctx = struct { bpf: *std.Build.Module, libbpf_step: *std.Build.Step.Compile, build_options: *std.Build.Step.Options, + btf_sanitizer: *std.Build.Step.Compile, }; pub fn build(b: *std.Build) !void { @@ -88,7 +117,7 @@ pub fn build(b: *std.Build) !void { const libbpf = create_libbpf(b, target, optimize); - const vmlinux = create_vmlinux(b); + const vmlinux, const btf_sanitizer = create_native_tools(b); const bpf = create_bpf(b, vmlinux); @@ -108,6 +137,7 @@ pub fn build(b: *std.Build) !void { .libbpf_step = libbpf, .build_options = build_options, .vmlinux = vmlinux, + .btf_sanitizer = btf_sanitizer, }; // default bpf program @@ -132,7 +162,7 @@ fn create_target_step(ctx: *const Ctx, main_path: []const u8, prog_path: []const .optimize = ctx.optimize, }); exe.root_module.addAnonymousImport("@bpf_prog", .{ - .root_source_file = prog.getEmittedBin(), + .root_source_file = prog, }); exe.root_module.addImport("bpf", ctx.bpf); exe.root_module.addImport("vmlinux", ctx.vmlinux); @@ -180,7 +210,7 @@ fn create_test_step(ctx: *const Ctx) !void { } const bpf_prog = create_bpf_prog(ctx, try fs.path.join(ctx.b.allocator, &[_][]const u8{ "samples", entry.name })); exe_tests.root_module.addAnonymousImport(try std.fmt.allocPrint(ctx.b.allocator, "@{s}", .{fs.path.stem(entry.name)}), .{ - .root_source_file = bpf_prog.getEmittedBin(), + .root_source_file = bpf_prog, }); } diff --git a/build.zig.zon b/build.zig.zon index d90c682..d72814a 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -7,5 +7,9 @@ .url = "https://github.com/tw4452852/libbpf_zig/archive/refs/tags/1.3.0-4.tar.gz", .hash = "1220c5343f425d2c8e0c9369de40f988ea67161c47d2f3432d55a115eba37a20bb6c", }, + .libelf = .{ + .url = "https://github.com/tw4452852/elfutils/archive/refs/tags/0.190.0-4.tar.gz", + .hash = "1220cbe3364fe97ec8d3704d0d30b69418ae955a776f43cc54e5471b4d3d44baeabb", + }, }, } diff --git a/src/btf_sanitizer/main.zig b/src/btf_sanitizer/main.zig new file mode 100644 index 0000000..1095459 --- /dev/null +++ b/src/btf_sanitizer/main.zig @@ -0,0 +1,124 @@ +const std = @import("std"); +const print = std.debug.print; +pub const c = @cImport({ + @cInclude("btf.h"); + @cInclude("libelf.h"); +}); + +// btf_sanitizer src_obj dst_obj +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + + const allocator = arena.allocator(); + + var it = std.process.args(); + _ = it.skip(); // skip process name + const src_obj_path = it.next().?; + const dst_obj_path = it.next().?; + + try std.fs.copyFileAbsolute(src_obj_path, dst_obj_path, .{}); + + const src_obj = try std.fs.openFileAbsolute(src_obj_path, .{ .mode = .read_only }); + defer src_obj.close(); + const dst_obj = try std.fs.openFileAbsolute(dst_obj_path, .{ .mode = .read_write }); + defer dst_obj.close(); + + const src_btf = c.btf__parse(src_obj_path, null) orelse { + print("failed to get source BTF: {}\n", .{std.posix.errno(-1)}); + return error.PARSE; + }; + var sz: c_uint = undefined; + + var buf = c.btf__raw_data(src_btf, &sz); + const dst_btf = c.btf__new(buf, sz) orelse { + print("create empty dest BTF failed: {}\n", .{std.posix.errno(-1)}); + return error.PARSE; + }; + defer c.btf__free(dst_btf); + + // sanitize + _ = c.btf__find_str(dst_btf, ""); // ensure btf is in modifiable/splited state + for (0..c.btf__type_cnt(dst_btf)) |i| { + const t: *c.btf_type = @constCast(c.btf__type_by_id(dst_btf, @intCast(i))); + + if (c.btf_is_fwd(t) or c.btf_is_struct(t)) { + // replace non-alphabet with '_' + const name: [:0]u8 = @constCast(std.mem.sliceTo(c.btf__name_by_offset(dst_btf, t.name_off), 0)); + for (name) |*ch| { + if (!std.ascii.isAlphabetic(ch.*)) { + ch.* = '_'; + } + } + } else if (c.btf_is_ptr(t)) { + // null pointer type name + t.name_off = 0; + } else if (c.btf_is_func_proto(t)) { + // add function parameter's name with 'argX' + const vlen: usize = @intCast(c.BTF_INFO_VLEN(@as(c_int, @bitCast(t.info)))); + const params: [*c]c.btf_param = @ptrFromInt(@intFromPtr(t) + @sizeOf(c.btf_type)); + + for (0..vlen) |pi| { + if (params[pi].name_off == 0) { + const name = try std.fmt.allocPrintZ(allocator, "arg{}", .{pi}); + var off = c.btf__find_str(dst_btf, name.ptr); + if (off < 0) { + off = c.btf__add_str(dst_btf, name.ptr); + if (off < 0) { + print("failed to add str\n", .{}); + return error.OOM; + } + } + + params[pi].name_off = @intCast(off); + } + } + } + } + buf = c.btf__raw_data(dst_btf, &sz); + + // update BTF section in elf + const elf = c.elf_begin(dst_obj.handle, c.ELF_C_RDWR, null) orelse { + print("failed to open dst elf: {}\n", .{std.posix.errno(-1)}); + return error.PARSE; + }; + defer { + _ = c.elf_update(elf, c.ELF_C_WRITE); + _ = c.elf_end(elf); + } + + var stridx: usize = undefined; + const ret = c.elf_getshdrstrndx(elf, &stridx); + if (ret != 0) { + print("failed to get string section idx: {}\n", .{std.posix.errno(-1)}); + return error.PARSE; + } + var scn = c.elf_nextscn(elf, null); + const btf_section_name = ".BTF"; + while (scn) |section| : (scn = c.elf_nextscn(elf, scn)) { + const shdr: *c.Elf64_Shdr = c.elf64_getshdr(section) orelse { + print("failed to get section header: {}\n", .{std.posix.errno(-1)}); + return error.PARSE; + }; + const name = c.elf_strptr(elf, stridx, shdr.sh_name) orelse { + print("failed to get section name: {}\n", .{std.posix.errno(-1)}); + return error.PARSE; + }; + if (std.mem.eql(u8, name[0..btf_section_name.len], btf_section_name)) { + var data: *c.Elf_Data = c.elf_newdata(section) orelse { + print("failed to create section data: {}\n", .{std.posix.errno(-1)}); + return error.OOM; + }; + + data.d_type = c.ELF_T_BYTE; + data.d_version = c.EV_CURRENT; + data.d_buf = @constCast(buf); + data.d_size = sz; + + break; + } + } else { + print("failed to find BTF section in elf\n", .{}); + return error.PARSE; + } +}