Commit 63a40bff47

Jakub Konka <kubkon@jakubkonka.com>
2024-04-16 21:53:24
link/elf: actually commit merge sections
1 parent b5a781d
Changed files (1)
src
src/link/Elf/merge_section.zig
@@ -0,0 +1,292 @@
+pub const MergeSection = struct {
+    name_offset: u32 = 0,
+    type: u32 = 0,
+    flags: u64 = 0,
+    output_section_index: u32 = 0,
+    bytes: std.ArrayListUnmanaged(u8) = .{},
+    table: std.HashMapUnmanaged(
+        String,
+        MergeSubsection.Index,
+        IndexContext,
+        std.hash_map.default_max_load_percentage,
+    ) = .{},
+    subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .{},
+
+    pub fn deinit(msec: *MergeSection, allocator: Allocator) void {
+        msec.bytes.deinit(allocator);
+        msec.table.deinit(allocator);
+        msec.subsections.deinit(allocator);
+    }
+
+    pub fn name(msec: MergeSection, elf_file: *Elf) [:0]const u8 {
+        return elf_file.strings.getAssumeExists(msec.name_offset);
+    }
+
+    pub fn address(msec: MergeSection, elf_file: *Elf) i64 {
+        const shdr = elf_file.shdrs.items[msec.output_section_index];
+        return @intCast(shdr.sh_addr);
+    }
+
+    const InsertResult = struct {
+        found_existing: bool,
+        key: String,
+        sub: *MergeSubsection.Index,
+    };
+
+    pub fn insert(msec: *MergeSection, allocator: Allocator, string: []const u8) !InsertResult {
+        const gop = try msec.table.getOrPutContextAdapted(
+            allocator,
+            string,
+            IndexAdapter{ .bytes = msec.bytes.items },
+            IndexContext{ .bytes = msec.bytes.items },
+        );
+        if (!gop.found_existing) {
+            const index: u32 = @intCast(msec.bytes.items.len);
+            try msec.bytes.appendSlice(allocator, string);
+            gop.key_ptr.* = .{ .pos = index, .len = @intCast(string.len) };
+        }
+        return .{ .found_existing = gop.found_existing, .key = gop.key_ptr.*, .sub = gop.value_ptr };
+    }
+
+    pub fn insertZ(msec: *MergeSection, allocator: Allocator, string: []const u8) !InsertResult {
+        const with_null = try allocator.alloc(u8, string.len + 1);
+        defer allocator.free(with_null);
+        @memcpy(with_null[0..string.len], string);
+        with_null[string.len] = 0;
+        return msec.insert(allocator, with_null);
+    }
+
+    /// Sorts all owned subsections.
+    /// Clears string table.
+    pub fn sort(msec: *MergeSection, elf_file: *Elf) !void {
+        const gpa = elf_file.base.comp.gpa;
+        try msec.subsections.ensureTotalCapacityPrecise(gpa, msec.table.count());
+
+        var it = msec.table.iterator();
+        while (it.next()) |entry| {
+            const msub = elf_file.mergeSubsection(entry.value_ptr.*);
+            if (!msub.alive) continue;
+            msec.subsections.appendAssumeCapacity(entry.value_ptr.*);
+        }
+        msec.table.clearAndFree(gpa);
+
+        const sortFn = struct {
+            pub fn sortFn(ctx: *Elf, lhs: MergeSubsection.Index, rhs: MergeSubsection.Index) bool {
+                const lhs_msub = ctx.mergeSubsection(lhs);
+                const rhs_msub = ctx.mergeSubsection(rhs);
+                if (lhs_msub.alignment.compareStrict(.eq, rhs_msub.alignment)) {
+                    if (lhs_msub.size == rhs_msub.size) {
+                        return mem.order(u8, lhs_msub.getString(ctx), rhs_msub.getString(ctx)) == .lt;
+                    }
+                    return lhs_msub.size < rhs_msub.size;
+                }
+                return lhs_msub.alignment.compareStrict(.lt, rhs_msub.alignment);
+            }
+        }.sortFn;
+
+        std.mem.sort(MergeSubsection.Index, msec.subsections.items, elf_file, sortFn);
+    }
+
+    pub const IndexContext = struct {
+        bytes: []const u8,
+
+        pub fn eql(_: @This(), a: String, b: String) bool {
+            return a.pos == b.pos;
+        }
+
+        pub fn hash(ctx: @This(), key: String) u64 {
+            const str = ctx.bytes[key.pos..][0..key.len];
+            return std.hash_map.hashString(str);
+        }
+    };
+
+    pub const IndexAdapter = struct {
+        bytes: []const u8,
+
+        pub fn eql(ctx: @This(), a: []const u8, b: String) bool {
+            const str = ctx.bytes[b.pos..][0..b.len];
+            return mem.eql(u8, a, str);
+        }
+
+        pub fn hash(_: @This(), adapted_key: []const u8) u64 {
+            return std.hash_map.hashString(adapted_key);
+        }
+    };
+
+    pub fn format(
+        msec: MergeSection,
+        comptime unused_fmt_string: []const u8,
+        options: std.fmt.FormatOptions,
+        writer: anytype,
+    ) !void {
+        _ = msec;
+        _ = unused_fmt_string;
+        _ = options;
+        _ = writer;
+        @compileError("do not format MergeSection directly");
+    }
+
+    pub fn fmt(msec: MergeSection, elf_file: *Elf) std.fmt.Formatter(format2) {
+        return .{ .data = .{
+            .msec = msec,
+            .elf_file = elf_file,
+        } };
+    }
+
+    const FormatContext = struct {
+        msec: MergeSection,
+        elf_file: *Elf,
+    };
+
+    pub fn format2(
+        ctx: FormatContext,
+        comptime unused_fmt_string: []const u8,
+        options: std.fmt.FormatOptions,
+        writer: anytype,
+    ) !void {
+        _ = options;
+        _ = unused_fmt_string;
+        const msec = ctx.msec;
+        const elf_file = ctx.elf_file;
+        try writer.print("{s} : @{x} : type({x}) : flags({x})\n", .{
+            msec.name(elf_file),
+            msec.address(elf_file),
+            msec.type,
+            msec.flags,
+        });
+        for (msec.subsections.items) |index| {
+            try writer.print("   {}\n", .{elf_file.mergeSubsection(index).fmt(elf_file)});
+        }
+    }
+
+    pub const Index = u32;
+};
+
+pub const MergeSubsection = struct {
+    value: i64 = 0,
+    merge_section_index: MergeSection.Index = 0,
+    string_index: u32 = 0,
+    size: u32 = 0,
+    alignment: Atom.Alignment = .@"1",
+    alive: bool = false,
+
+    pub fn address(msub: MergeSubsection, elf_file: *Elf) i64 {
+        return msub.mergeSection(elf_file).address(elf_file) + msub.value;
+    }
+
+    pub fn mergeSection(msub: MergeSubsection, elf_file: *Elf) *MergeSection {
+        return elf_file.mergeSection(msub.merge_section_index);
+    }
+
+    pub fn getString(msub: MergeSubsection, elf_file: *Elf) []const u8 {
+        const msec = msub.mergeSection(elf_file);
+        return msec.bytes.items[msub.string_index..][0..msub.size];
+    }
+
+    pub fn format(
+        msub: MergeSubsection,
+        comptime unused_fmt_string: []const u8,
+        options: std.fmt.FormatOptions,
+        writer: anytype,
+    ) !void {
+        _ = msub;
+        _ = unused_fmt_string;
+        _ = options;
+        _ = writer;
+        @compileError("do not format MergeSubsection directly");
+    }
+
+    pub fn fmt(msub: MergeSubsection, elf_file: *Elf) std.fmt.Formatter(format2) {
+        return .{ .data = .{
+            .msub = msub,
+            .elf_file = elf_file,
+        } };
+    }
+
+    const FormatContext = struct {
+        msub: MergeSubsection,
+        elf_file: *Elf,
+    };
+
+    pub fn format2(
+        ctx: FormatContext,
+        comptime unused_fmt_string: []const u8,
+        options: std.fmt.FormatOptions,
+        writer: anytype,
+    ) !void {
+        _ = options;
+        _ = unused_fmt_string;
+        const msub = ctx.msub;
+        const elf_file = ctx.elf_file;
+        try writer.print("@{x} : align({x}) : size({x})", .{
+            msub.address(elf_file),
+            msub.alignment,
+            msub.size,
+        });
+        if (!msub.alive) try writer.writeAll(" : [*]");
+    }
+
+    pub const Index = u32;
+};
+
+pub const InputMergeSection = struct {
+    merge_section_index: MergeSection.Index = 0,
+    atom_index: Atom.Index = 0,
+    offsets: std.ArrayListUnmanaged(u32) = .{},
+    subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .{},
+    bytes: std.ArrayListUnmanaged(u8) = .{},
+    strings: std.ArrayListUnmanaged(String) = .{},
+
+    pub fn deinit(imsec: *InputMergeSection, allocator: Allocator) void {
+        imsec.offsets.deinit(allocator);
+        imsec.subsections.deinit(allocator);
+        imsec.bytes.deinit(allocator);
+        imsec.strings.deinit(allocator);
+    }
+
+    pub fn clearAndFree(imsec: *InputMergeSection, allocator: Allocator) void {
+        imsec.bytes.clearAndFree(allocator);
+        // TODO: imsec.strings.clearAndFree(allocator);
+    }
+
+    pub fn findSubsection(imsec: InputMergeSection, offset: u32) ?struct { MergeSubsection.Index, u32 } {
+        // TODO: binary search
+        for (imsec.offsets.items, 0..) |off, index| {
+            if (offset < off) return .{
+                imsec.subsections.items[index - 1],
+                offset - imsec.offsets.items[index - 1],
+            };
+        }
+        const last = imsec.offsets.items.len - 1;
+        const last_off = imsec.offsets.items[last];
+        const last_len = imsec.strings.items[last].len;
+        if (offset < last_off + last_len) return .{ imsec.subsections.items[last], offset - last_off };
+        return null;
+    }
+
+    pub fn insert(imsec: *InputMergeSection, allocator: Allocator, string: []const u8) !void {
+        const index: u32 = @intCast(imsec.bytes.items.len);
+        try imsec.bytes.appendSlice(allocator, string);
+        try imsec.strings.append(allocator, .{ .pos = index, .len = @intCast(string.len) });
+    }
+
+    pub fn insertZ(imsec: *InputMergeSection, allocator: Allocator, string: []const u8) !void {
+        const index: u32 = @intCast(imsec.bytes.items.len);
+        try imsec.bytes.ensureUnusedCapacity(allocator, string.len + 1);
+        imsec.bytes.appendSliceAssumeCapacity(string);
+        imsec.bytes.appendAssumeCapacity(0);
+        try imsec.strings.append(allocator, .{ .pos = index, .len = @intCast(string.len + 1) });
+    }
+
+    pub const Index = u32;
+};
+
+const String = struct { pos: u32, len: u32 };
+
+const assert = std.debug.assert;
+const mem = std.mem;
+const std = @import("std");
+
+const Allocator = mem.Allocator;
+const Atom = @import("Atom.zig");
+const Elf = @import("../Elf.zig");