diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2024-04-16 21:53:24 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2024-04-20 23:36:42 +0200 |
| commit | 63a40bff47ce49a0cd27ef2adf99fa5e316fa8f9 (patch) | |
| tree | a0ad82e502c3355d0fb3d533a4cd34c7296c84e7 /src | |
| parent | b5a781d19d3de1a50cbefe0df6f42a6ac8a05299 (diff) | |
| download | zig-63a40bff47ce49a0cd27ef2adf99fa5e316fa8f9.tar.gz zig-63a40bff47ce49a0cd27ef2adf99fa5e316fa8f9.zip | |
link/elf: actually commit merge sections
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/Elf/merge_section.zig | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/src/link/Elf/merge_section.zig b/src/link/Elf/merge_section.zig new file mode 100644 index 0000000000..f3be74b3ad --- /dev/null +++ b/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"); |
