Skip to content

Commit

Permalink
create btf sanitizer to fixup invalid btf generated from zig
Browse files Browse the repository at this point in the history
Signed-off-by: Tw <[email protected]>
  • Loading branch information
tw4452852 committed Jul 6, 2024
1 parent 0dc66ee commit 866d83c
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 12 deletions.
54 changes: 42 additions & 12 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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(.{
Expand All @@ -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"),
Expand All @@ -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"),
Expand All @@ -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 {
Expand All @@ -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);

Expand All @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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,
});
}

Expand Down
4 changes: 4 additions & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
},
}
124 changes: 124 additions & 0 deletions src/btf_sanitizer/main.zig
Original file line number Diff line number Diff line change
@@ -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;
}
}

0 comments on commit 866d83c

Please sign in to comment.