aboutsummaryrefslogtreecommitdiff
path: root/toolchain/akibabuilder/main.zig
blob: 8bd07fab8518baac2499241d4ffd45ea6af86d91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
const std = @import("std");

// Akiba magic signature
const AKIBA_MAGIC = [8]u8{ 'A', 'K', 'I', 'B', 'A', 'E', 'L', 'F' };
const AKIBA_VERSION: u32 = 1;

// Executable types
const AKIBA_TYPE_CLI: u32 = 0;
const AKIBA_TYPE_GUI: u32 = 1;
const AKIBA_TYPE_SERVICE: u32 = 2;
const AKIBA_TYPE_LIBRARY: u32 = 3;

// Akiba executable header (64 bytes)
const AkibaHeader = extern struct {
    magic: [8]u8,
    version: u32,
    exec_type: u32,
    elf_offset: u64,
    elf_size: u64,
    metadata_offset: u64,
    metadata_size: u64,
    entry_point: u64,
    reserved: [16]u8,
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const args = try std.process.argsAlloc(allocator);
    defer std.process.argsFree(allocator, args);

    if (args.len < 3) {
        std.debug.print("Usage: akibabuilder <input.elf> <output.akiba> [type]\n", .{});
        std.debug.print("Types: cli (default), gui, service, library\n", .{});
        return;
    }

    const input_path = args[1];
    const output_path = args[2];
    const exec_type = if (args.len >= 4) parse_type(args[3]) else AKIBA_TYPE_CLI;

    try wrap_elf(allocator, input_path, output_path, exec_type);
}

fn parse_type(type_str: []const u8) u32 {
    if (std.mem.eql(u8, type_str, "gui")) return AKIBA_TYPE_GUI;
    if (std.mem.eql(u8, type_str, "service")) return AKIBA_TYPE_SERVICE;
    if (std.mem.eql(u8, type_str, "library")) return AKIBA_TYPE_LIBRARY;
    return AKIBA_TYPE_CLI;
}

fn wrap_elf(allocator: std.mem.Allocator, input_path: []const u8, output_path: []const u8, exec_type: u32) !void {
    // Read ELF file
    const elf_data = try std.fs.cwd().readFileAlloc(allocator, input_path, 10 * 1024 * 1024);
    defer allocator.free(elf_data);

    std.debug.print("Read {d} bytes from {s}\n", .{ elf_data.len, input_path });

    // Parse ELF to get entry point
    const entry_point = try parse_elf_entry(elf_data);
    std.debug.print("Entry point: 0x{X:0>16}\n", .{entry_point});

    // Create Akiba header
    const header = AkibaHeader{
        .magic = AKIBA_MAGIC,
        .version = AKIBA_VERSION,
        .exec_type = exec_type,
        .elf_offset = @sizeOf(AkibaHeader),
        .elf_size = elf_data.len,
        .metadata_offset = 0,
        .metadata_size = 0,
        .entry_point = entry_point,
        .reserved = [_]u8{0} ** 16,
    };

    // Write output file
    const output_file = try std.fs.cwd().createFile(output_path, .{});
    defer output_file.close();

    // Write header as bytes
    const header_bytes = std.mem.asBytes(&header);
    try output_file.writeAll(header_bytes);

    // Write ELF data
    try output_file.writeAll(elf_data);

    std.debug.print("Created {s} ({d} bytes)\n", .{ output_path, header_bytes.len + elf_data.len });
    std.debug.print("Type: {s}\n", .{type_name(exec_type)});
}

fn parse_elf_entry(elf_data: []const u8) !u64 {
    if (elf_data.len < 32) return error.TooSmall;

    // ELF magic check
    if (elf_data[0] != 0x7F or elf_data[1] != 'E' or elf_data[2] != 'L' or elf_data[3] != 'F') {
        return error.NotELF;
    }

    // Check 64-bit
    if (elf_data[4] != 2) {
        return error.Not64Bit;
    }

    // Entry point is at offset 24 for ELF64
    const entry_bytes = elf_data[24..32];
    return std.mem.readInt(u64, entry_bytes[0..8], .little);
}

fn type_name(exec_type: u32) []const u8 {
    return switch (exec_type) {
        AKIBA_TYPE_CLI => "CLI",
        AKIBA_TYPE_GUI => "GUI",
        AKIBA_TYPE_SERVICE => "Service",
        AKIBA_TYPE_LIBRARY => "Library",
        else => "Unknown",
    };
}