mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-27 13:58:20 +00:00
build: rewrite ksyms script in zig
This commit is contained in:
@@ -36,11 +36,19 @@ pub fn build(b: *std.Build) void {
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
// first pass
|
||||
const empty_cmd = b.addSystemCommand(&.{ "python3", "build/ksyms.py", "--empty" });
|
||||
empty_cmd.addArg("--output");
|
||||
const ksyms_exe = b.addExecutable(.{
|
||||
.name = "ksyms",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("build/ksyms.zig"),
|
||||
.target = b.graph.host,
|
||||
}),
|
||||
});
|
||||
|
||||
const empty_cmd = b.addRunArtifact(ksyms_exe);
|
||||
const empty_ksyms_s = empty_cmd.addOutputFileArg("ksyms_empty.s");
|
||||
|
||||
empty_cmd.stdio = .inherit;
|
||||
|
||||
const rtl_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/rtl/root.zig"),
|
||||
.optimize = optimize,
|
||||
@@ -69,12 +77,10 @@ pub fn build(b: *std.Build) void {
|
||||
b.step("kernel-nosym", "Build nosym kernel").dependOn(&kernel_nosym.step);
|
||||
|
||||
// generate symbols
|
||||
const ksyms = b.addSystemCommand(&.{ "python3", "build/ksyms.py" });
|
||||
ksyms.addArg("--input");
|
||||
const ksyms = b.addRunArtifact(ksyms_exe);
|
||||
ksyms.step.dependOn(&kernel_nosym.step);
|
||||
ksyms.addFileArg(kernel_nosym.getEmittedBin());
|
||||
ksyms.addArg("--output");
|
||||
const ksyms_s = ksyms.addOutputFileArg("ksyms.s");
|
||||
ksyms.addFileArg(kernel_nosym.getEmittedBin());
|
||||
|
||||
// second pass
|
||||
const kernel = addKernel(b, plat, optimize, config_module, ksyms_module, rtl_module, arch_module);
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Simple kernel symbol dumper
|
||||
# Usage: ksyms.py --input <kernel binary> --output <output file>
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
excluded_symbols = [
|
||||
"text_start_addr",
|
||||
"text_end_addr",
|
||||
"rodata_start_addr",
|
||||
"rodata_end_addr",
|
||||
"data_start_addr",
|
||||
"data_end_addr",
|
||||
]
|
||||
|
||||
|
||||
def parse_nm_line(line: str):
|
||||
parts = line.split()
|
||||
if len(parts) < 3:
|
||||
return None
|
||||
|
||||
addr_str, type, name = parts[0], parts[1], parts[2]
|
||||
|
||||
# Only get symbols in text
|
||||
if type.lower() != "t":
|
||||
return None
|
||||
|
||||
if name in excluded_symbols:
|
||||
return None
|
||||
|
||||
addr = int(addr_str, 16)
|
||||
return addr, name
|
||||
|
||||
|
||||
def run_nm(binary: str):
|
||||
result = subprocess.run(["nm", "-n", binary], capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"nm failed: {result.stderr}")
|
||||
return result.stdout.splitlines()
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description="Generate kernel symbols from a binary")
|
||||
parser.add_argument("--input", help="Input kernel binary")
|
||||
parser.add_argument("--output", help="Output Assembly file (.s)")
|
||||
parser.add_argument("--empty", action="store_true", help="Generate empty ksyms")
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.output, "w") as f:
|
||||
f.write("/* Auto-generated by build/ksyms.py - do not edit */\n")
|
||||
f.write(".section .rodata\n")
|
||||
|
||||
if args.empty:
|
||||
f.write(".global ksyms_array\n")
|
||||
f.write(".global ksyms_count\n")
|
||||
f.write(".align 8\n")
|
||||
f.write("ksyms_array:\n")
|
||||
f.write("ksyms_count:\n")
|
||||
f.write(" .quad 0\n")
|
||||
sys.exit(0)
|
||||
|
||||
lines = run_nm(args.input)
|
||||
symbols = []
|
||||
for line in lines:
|
||||
parsed = parse_nm_line(line)
|
||||
if parsed:
|
||||
symbols.append(parsed)
|
||||
|
||||
# Sort by address
|
||||
symbols.sort(key=lambda x: x[0])
|
||||
|
||||
for i, (addr, name) in enumerate(symbols):
|
||||
escaped = name.replace("\\", "\\\\").replace('"', '\\"')
|
||||
f.write(f'.Lstr{i}: .asciz "{escaped}"\n')
|
||||
|
||||
f.write("\n.global ksyms_array\n")
|
||||
f.write(".global ksyms_count\n")
|
||||
f.write(".align 8\n")
|
||||
f.write("ksyms_array:\n")
|
||||
|
||||
for i, (addr, name) in enumerate(symbols):
|
||||
name_len = len(name.encode("utf-8"))
|
||||
f.write(f" .quad 0x{addr:016x}\n")
|
||||
f.write(f" .quad .Lstr{i}\n")
|
||||
f.write(f" .quad {name_len}\n")
|
||||
|
||||
f.write("\nksyms_count:\n")
|
||||
f.write(f" .quad {len(symbols)}\n")
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
//! Script to generate ksyms
|
||||
//! Usage: ksyms.zig <output> <input>
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const elf = std.elf;
|
||||
const Io = std.Io;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Symbol = struct {
|
||||
addr: usize,
|
||||
name: []const u8,
|
||||
};
|
||||
|
||||
const file_hdr =
|
||||
\\/* Auto-generated by build/ksyms.zig - do not edit */
|
||||
\\.section .rodata
|
||||
\\.global ksyms_array
|
||||
\\.global ksyms_count
|
||||
\\.align 8
|
||||
\\
|
||||
;
|
||||
|
||||
const empty_str = file_hdr ++
|
||||
\\ksyms_array:
|
||||
\\ksyms_count:
|
||||
\\.quad 0
|
||||
;
|
||||
|
||||
fn getElfString(strtab: []const u8, offset: u32) []const u8 {
|
||||
const remainder = strtab[offset..];
|
||||
const str_ptr: [*:0]const u8 = @ptrCast(remainder.ptr);
|
||||
const len = std.mem.len(str_ptr);
|
||||
return remainder[0..len];
|
||||
}
|
||||
|
||||
fn getSymbols(allocator: Allocator, bytes: []u8) !std.ArrayList(Symbol) {
|
||||
var reader = Io.Reader.fixed(bytes);
|
||||
|
||||
const header = try std.elf.Header.read(&reader);
|
||||
|
||||
const shstrhdr_addr = header.shoff + (header.shentsize * header.shstrndx);
|
||||
|
||||
const shstrhdr = std.mem.bytesAsValue(elf.Shdr, bytes[shstrhdr_addr..]);
|
||||
const section_names: []const u8 = bytes[shstrhdr.sh_offset..];
|
||||
|
||||
reader.seek = header.shoff;
|
||||
|
||||
var text_section_index: usize = undefined;
|
||||
var symtab: elf.Shdr = undefined;
|
||||
|
||||
const shdrs = std.mem.bytesAsSlice(
|
||||
elf.Shdr,
|
||||
bytes[header.shoff..][0 .. header.shnum * header.shentsize],
|
||||
);
|
||||
|
||||
for (0..header.shnum) |i| {
|
||||
const name = getElfString(section_names, shdrs[i].sh_name);
|
||||
|
||||
if (std.mem.eql(u8, name, ".text")) {
|
||||
text_section_index = i;
|
||||
} else if (shdrs[i].sh_type == elf.SHT_SYMTAB) {
|
||||
symtab = shdrs[i];
|
||||
}
|
||||
}
|
||||
|
||||
const sym_strtab_shdr_addr = header.shoff + (header.shentsize * symtab.sh_link);
|
||||
const sym_strtab_shdr = std.mem.bytesAsValue(elf.Shdr, bytes[sym_strtab_shdr_addr..]);
|
||||
const symbol_names: []const u8 = bytes[sym_strtab_shdr.sh_offset..];
|
||||
|
||||
const how_many = symtab.sh_size / symtab.sh_entsize;
|
||||
const symbols: [*]elf.Sym = @ptrCast(@alignCast(bytes[symtab.sh_offset..].ptr));
|
||||
|
||||
var ret = try std.ArrayList(Symbol).initCapacity(allocator, how_many);
|
||||
|
||||
for (0..how_many) |i| {
|
||||
const sym = symbols[i];
|
||||
|
||||
if (sym.st_shndx == elf.SHN_UNDEF)
|
||||
continue;
|
||||
|
||||
if (sym.st_name == 0)
|
||||
continue;
|
||||
|
||||
if (sym.st_shndx != text_section_index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const name = getElfString(symbol_names, sym.st_name);
|
||||
|
||||
try ret.append(allocator, .{
|
||||
.addr = sym.st_value,
|
||||
.name = name,
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn lessThan(_: void, a: Symbol, b: Symbol) bool {
|
||||
return a.addr < b.addr;
|
||||
}
|
||||
|
||||
fn generateSymbols(alloc: Allocator, file: []u8, writer: *Io.Writer) !void {
|
||||
const symbols = try getSymbols(alloc, file);
|
||||
|
||||
std.mem.sort(Symbol, symbols.items, {}, lessThan);
|
||||
|
||||
_ = try writer.write(file_hdr);
|
||||
_ = try writer.write("ksyms_array:\n");
|
||||
|
||||
for (symbols.items, 0..) |sym, i| {
|
||||
try writer.print(".quad 0x{x}\n", .{sym.addr});
|
||||
try writer.print(".quad .Lstr{}\n", .{i});
|
||||
try writer.print(".quad {}\n", .{sym.name.len});
|
||||
}
|
||||
|
||||
try writer.print("ksyms_count:\n .quad {}\n", .{symbols.items.len});
|
||||
|
||||
for (symbols.items, 0..) |sym, i| {
|
||||
if (std.mem.indexOfScalar(u8, sym.name, '"') == null) {
|
||||
try writer.print(".Lstr{}: .asciz \"{s}\"\n", .{ i, sym.name });
|
||||
} else {
|
||||
const size = std.mem.replacementSize(u8, sym.name, "\"", "\\\"");
|
||||
const escaped = try alloc.alloc(u8, size);
|
||||
|
||||
_ = std.mem.replace(u8, sym.name, "\"", "\\\"", escaped);
|
||||
try writer.print(".Lstr{}: .asciz \"{s}\" \n", .{ i, escaped });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
const arena = init.arena.allocator();
|
||||
const io = init.io;
|
||||
const args = init.minimal.args;
|
||||
const args_slice = try args.toSlice(arena);
|
||||
|
||||
const outfile = try std.Io.Dir.cwd().createFile(io, args_slice[1], .{});
|
||||
defer outfile.close(io);
|
||||
|
||||
const write_buffer = try arena.alloc(u8, 64 * 1024);
|
||||
var writer = outfile.writer(io, write_buffer);
|
||||
|
||||
if (args_slice.len == 2) {
|
||||
// Only output, generate an empty file.
|
||||
try writer.interface.writeAll(empty_str);
|
||||
}
|
||||
|
||||
if (args_slice.len == 3) {
|
||||
// Got an input, parse the ELF.
|
||||
const buf = try std.Io.Dir.cwd().readFileAlloc(
|
||||
io,
|
||||
args_slice[2],
|
||||
arena,
|
||||
.unlimited,
|
||||
);
|
||||
try generateSymbols(arena, buf, &writer.interface);
|
||||
}
|
||||
|
||||
try writer.flush();
|
||||
}
|
||||
Reference in New Issue
Block a user