diff --git a/CMakeLists.txt b/CMakeLists.txt index e71f799b549187b51897c1df5870a09c54157e6f..70d1fde6bb5b83d4e13984ee86c8bda84ca6ca8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -603,18 +603,18 @@ set(ZIG_STAGE2_SOURCES src/link/Elf/AtomList.zig src/link/Elf/LdScript.zig src/link/Elf/LinkerDefined.zig + src/link/Elf/Merge.zig src/link/Elf/Object.zig src/link/Elf/SharedObject.zig src/link/Elf/Symbol.zig + src/link/Elf/Thunk.zig src/link/Elf/ZigObject.zig src/link/Elf/eh_frame.zig src/link/Elf/file.zig src/link/Elf/gc.zig - src/link/Elf/merge_section.zig src/link/Elf/relocatable.zig src/link/Elf/relocation.zig src/link/Elf/synthetic_sections.zig - src/link/Elf/Thunk.zig src/link/MachO.zig src/link/MachO/Archive.zig src/link/MachO/Atom.zig diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a69f5fe18c3425cee25b285d581e0f55d19f7fcf..24a99e567759b33eef48458967e6e159b5c41df4 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1,3 +1,5 @@ +pub const Atom = @import("Elf/Atom.zig"); + base: link.File, rpath_table: std.StringArrayHashMapUnmanaged(void), image_base: u64, @@ -109,8 +111,8 @@ num_ifunc_dynrelocs: usize = 0, thunks: std.ArrayListUnmanaged(Thunk) = .empty, /// List of output merge sections with deduped contents. -merge_sections: std.ArrayListUnmanaged(MergeSection) = .empty, -comment_merge_section_index: ?MergeSection.Index = null, +merge_sections: std.ArrayListUnmanaged(Merge.Section) = .empty, +comment_merge_section_index: ?Merge.Section.Index = null, first_eflags: ?elf.Elf64_Word = null, @@ -4726,7 +4728,7 @@ pub fn linkerDefinedPtr(self: *Elf) ?*LinkerDefined { return self.file(index).?.linker_defined; } -pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"type": u32) !MergeSection.Index { +pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"type": u32) !Merge.Section.Index { const gpa = self.base.comp.gpa; const out_name = name: { if (self.base.isRelocatable()) break :name name; @@ -4739,7 +4741,7 @@ pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"typ } const out_off = try self.insertShString(out_name); const out_flags = flags & ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP); - const index = @as(MergeSection.Index, @intCast(self.merge_sections.items.len)); + const index: Merge.Section.Index = @intCast(self.merge_sections.items.len); const msec = try self.merge_sections.addOne(gpa); msec.* = .{ .name_offset = out_off, @@ -4749,7 +4751,7 @@ pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"typ return index; } -pub fn mergeSection(self: *Elf, index: MergeSection.Index) *MergeSection { +pub fn mergeSection(self: *Elf, index: Merge.Section.Index) *Merge.Section { assert(index < self.merge_sections.items.len); return &self.merge_sections.items[index]; } @@ -5541,6 +5543,9 @@ const relocs_log = std.log.scoped(.link_relocs); const state_log = std.log.scoped(.link_state); const math = std.math; const mem = std.mem; +const Allocator = std.mem.Allocator; +const Cache = std.Build.Cache; +const Hash = std.hash.Wyhash; const codegen = @import("../codegen.zig"); const dev = @import("../dev.zig"); @@ -5548,7 +5553,6 @@ const eh_frame = @import("Elf/eh_frame.zig"); const gc = @import("Elf/gc.zig"); const glibc = @import("../glibc.zig"); const link = @import("../link.zig"); -const merge_section = @import("Elf/merge_section.zig"); const musl = @import("../musl.zig"); const relocatable = @import("Elf/relocatable.zig"); const relocation = @import("Elf/relocation.zig"); @@ -5556,12 +5560,10 @@ const target_util = @import("../target.zig"); const trace = @import("../tracy.zig").trace; const synthetic_sections = @import("Elf/synthetic_sections.zig"); +const Merge = @import("Elf/Merge.zig"); const Air = @import("../Air.zig"); -const Allocator = std.mem.Allocator; const Archive = @import("Elf/Archive.zig"); -pub const Atom = @import("Elf/Atom.zig"); const AtomList = @import("Elf/AtomList.zig"); -const Cache = std.Build.Cache; const Path = Cache.Path; const Compilation = @import("../Compilation.zig"); const ComdatGroupSection = synthetic_sections.ComdatGroupSection; @@ -5574,15 +5576,11 @@ const File = @import("Elf/file.zig").File; const GnuHashSection = synthetic_sections.GnuHashSection; const GotSection = synthetic_sections.GotSection; const GotPltSection = synthetic_sections.GotPltSection; -const Hash = std.hash.Wyhash; const HashSection = synthetic_sections.HashSection; -const InputMergeSection = merge_section.InputMergeSection; const LdScript = @import("Elf/LdScript.zig"); const LinkerDefined = @import("Elf/LinkerDefined.zig"); const Liveness = @import("../Liveness.zig"); const LlvmObject = @import("../codegen/llvm.zig").Object; -const MergeSection = merge_section.MergeSection; -const MergeSubsection = merge_section.MergeSubsection; const Zcu = @import("../Zcu.zig"); const Object = @import("Elf/Object.zig"); const InternPool = @import("../InternPool.zig"); diff --git a/src/link/Elf/Merge.zig b/src/link/Elf/Merge.zig new file mode 100644 index 0000000000000000000000000000000000000000..33e4f9c5b29cc220986d9ccb59ce5b8d8a2a5aa2 --- /dev/null +++ b/src/link/Elf/Merge.zig @@ -0,0 +1,341 @@ +pub const Section = struct { + value: u64 = 0, + size: u64 = 0, + alignment: Atom.Alignment = .@"1", + entsize: u32 = 0, + name_offset: u32 = 0, + type: u32 = 0, + flags: u64 = 0, + output_section_index: u32 = 0, + bytes: std.ArrayListUnmanaged(u8) = .empty, + table: std.HashMapUnmanaged( + String, + Subsection.Index, + IndexContext, + std.hash_map.default_max_load_percentage, + ) = .{}, + subsections: std.ArrayListUnmanaged(Subsection) = .empty, + finalized_subsections: std.ArrayListUnmanaged(Subsection.Index) = .empty, + + pub fn deinit(msec: *Section, allocator: Allocator) void { + msec.bytes.deinit(allocator); + msec.table.deinit(allocator); + msec.subsections.deinit(allocator); + msec.finalized_subsections.deinit(allocator); + } + + pub fn name(msec: Section, elf_file: *Elf) [:0]const u8 { + return elf_file.getShString(msec.name_offset); + } + + pub fn address(msec: Section, elf_file: *Elf) i64 { + const shdr = elf_file.sections.items(.shdr)[msec.output_section_index]; + return @intCast(shdr.sh_addr + msec.value); + } + + const InsertResult = struct { + found_existing: bool, + key: String, + sub: *Subsection.Index, + }; + + pub fn insert(msec: *Section, 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: *Section, 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); + } + + /// Finalizes the merge section and clears hash table. + /// Sorts all owned subsections. + pub fn finalize(msec: *Section, allocator: Allocator) !void { + try msec.finalized_subsections.ensureTotalCapacityPrecise(allocator, msec.subsections.items.len); + + var it = msec.table.iterator(); + while (it.next()) |entry| { + const msub = msec.mergeSubsection(entry.value_ptr.*); + if (!msub.alive) continue; + msec.finalized_subsections.appendAssumeCapacity(entry.value_ptr.*); + } + msec.table.clearAndFree(allocator); + + const sortFn = struct { + pub fn sortFn(ctx: *Section, lhs: Subsection.Index, rhs: Subsection.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) { + const lhs_string = ctx.bytes.items[lhs_msub.string_index..][0..lhs_msub.size]; + const rhs_string = ctx.bytes.items[rhs_msub.string_index..][0..rhs_msub.size]; + return mem.order(u8, lhs_string, rhs_string) == .lt; + } + return lhs_msub.size < rhs_msub.size; + } + return lhs_msub.alignment.compareStrict(.lt, rhs_msub.alignment); + } + }.sortFn; + + std.mem.sort(Subsection.Index, msec.finalized_subsections.items, msec, sortFn); + } + + pub fn updateSize(msec: *Section) void { + // TODO a 'stale' flag would be better here perhaps? + msec.size = 0; + msec.alignment = .@"1"; + msec.entsize = 0; + for (msec.finalized_subsections.items) |msub_index| { + const msub = msec.mergeSubsection(msub_index); + assert(msub.alive); + const offset = msub.alignment.forward(msec.size); + const padding = offset - msec.size; + msub.value = @intCast(offset); + msec.size += padding + msub.size; + msec.alignment = msec.alignment.max(msub.alignment); + msec.entsize = if (msec.entsize == 0) msub.entsize else @min(msec.entsize, msub.entsize); + } + } + + pub fn initOutputSection(msec: *Section, elf_file: *Elf) !void { + msec.output_section_index = elf_file.sectionByName(msec.name(elf_file)) orelse try elf_file.addSection(.{ + .name = msec.name_offset, + .type = msec.type, + .flags = msec.flags, + }); + } + + pub fn addMergeSubsection(msec: *Section, allocator: Allocator) !Subsection.Index { + const index: Subsection.Index = @intCast(msec.subsections.items.len); + const msub = try msec.subsections.addOne(allocator); + msub.* = .{}; + return index; + } + + pub fn mergeSubsection(msec: *Section, index: Subsection.Index) *Subsection { + assert(index < msec.subsections.items.len); + return &msec.subsections.items[index]; + } + + 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: Section, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = msec; + _ = unused_fmt_string; + _ = options; + _ = writer; + @compileError("do not format directly"); + } + + pub fn fmt(msec: Section, elf_file: *Elf) std.fmt.Formatter(format2) { + return .{ .data = .{ + .msec = msec, + .elf_file = elf_file, + } }; + } + + const FormatContext = struct { + msec: Section, + 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} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{ + msec.name(elf_file), + msec.address(elf_file), + msec.size, + msec.alignment.toByteUnits() orelse 0, + msec.entsize, + msec.type, + msec.flags, + }); + for (msec.subsections.items) |msub| { + try writer.print(" {}\n", .{msub.fmt(elf_file)}); + } + } + + pub const Index = u32; +}; + +pub const Subsection = struct { + value: i64 = 0, + merge_section_index: Section.Index = 0, + string_index: u32 = 0, + size: u32 = 0, + alignment: Atom.Alignment = .@"1", + entsize: u32 = 0, + alive: bool = false, + + pub fn address(msub: Subsection, elf_file: *Elf) i64 { + return msub.mergeSection(elf_file).address(elf_file) + msub.value; + } + + pub fn mergeSection(msub: Subsection, elf_file: *Elf) *Section { + return elf_file.mergeSection(msub.merge_section_index); + } + + pub fn getString(msub: Subsection, 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: Subsection, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = msub; + _ = unused_fmt_string; + _ = options; + _ = writer; + @compileError("do not format directly"); + } + + pub fn fmt(msub: Subsection, elf_file: *Elf) std.fmt.Formatter(format2) { + return .{ .data = .{ + .msub = msub, + .elf_file = elf_file, + } }; + } + + const FormatContext = struct { + msub: Subsection, + 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 InputSection = struct { + merge_section_index: Section.Index = 0, + atom_index: Atom.Index = 0, + offsets: std.ArrayListUnmanaged(u32) = .empty, + subsections: std.ArrayListUnmanaged(Subsection.Index) = .empty, + bytes: std.ArrayListUnmanaged(u8) = .empty, + strings: std.ArrayListUnmanaged(String) = .empty, + + pub fn deinit(imsec: *InputSection, allocator: Allocator) void { + imsec.offsets.deinit(allocator); + imsec.subsections.deinit(allocator); + imsec.bytes.deinit(allocator); + imsec.strings.deinit(allocator); + } + + pub fn clearAndFree(imsec: *InputSection, allocator: Allocator) void { + imsec.bytes.clearAndFree(allocator); + // TODO: imsec.strings.clearAndFree(allocator); + } + + const FindSubsectionResult = struct { + msub_index: Subsection.Index, + offset: u32, + }; + + pub fn findSubsection(imsec: InputSection, offset: u32) ?FindSubsectionResult { + // TODO: binary search + for (imsec.offsets.items, 0..) |off, index| { + if (offset < off) return .{ + .msub_index = imsec.subsections.items[index - 1], + .offset = 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 .{ + .msub_index = imsec.subsections.items[last], + .offset = offset - last_off, + }; + return null; + } + + pub fn insert(imsec: *InputSection, 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 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"); +const Merge = @This(); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 03172efb386a9bca16ba649807c35405b8f79ba5..d1f1870036d5c16752f39b590df4eb966d73e986 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -23,8 +23,8 @@ atoms_extra: std.ArrayListUnmanaged(u32) = .empty, comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup) = .empty, comdat_group_data: std.ArrayListUnmanaged(u32) = .empty, -input_merge_sections: std.ArrayListUnmanaged(InputMergeSection) = .empty, -input_merge_sections_indexes: std.ArrayListUnmanaged(InputMergeSection.Index) = .empty, +input_merge_sections: std.ArrayListUnmanaged(Merge.InputSection) = .empty, +input_merge_sections_indexes: std.ArrayListUnmanaged(Merge.InputSection.Index) = .empty, fdes: std.ArrayListUnmanaged(Fde) = .empty, cies: std.ArrayListUnmanaged(Cie) = .empty, @@ -1305,14 +1305,14 @@ fn setAtomFields(o: *Object, atom_ptr: *Atom, opts: Atom.Extra.AsOptionals) void o.setAtomExtra(atom_ptr.extra_index, extras); } -fn addInputMergeSection(self: *Object, allocator: Allocator) !InputMergeSection.Index { - const index: InputMergeSection.Index = @intCast(self.input_merge_sections.items.len); +fn addInputMergeSection(self: *Object, allocator: Allocator) !Merge.InputSection.Index { + const index: Merge.InputSection.Index = @intCast(self.input_merge_sections.items.len); const msec = try self.input_merge_sections.addOne(allocator); msec.* = .{}; return index; } -fn inputMergeSection(self: *Object, index: InputMergeSection.Index) ?*InputMergeSection { +fn inputMergeSection(self: *Object, index: Merge.InputSection.Index) ?*Merge.InputSection { if (index == 0) return null; return &self.input_merge_sections.items[index]; } @@ -1522,6 +1522,6 @@ const Cie = eh_frame.Cie; const Elf = @import("../Elf.zig"); const Fde = eh_frame.Fde; const File = @import("file.zig").File; -const InputMergeSection = @import("merge_section.zig").InputMergeSection; +const Merge = @import("Merge.zig"); const Symbol = @import("Symbol.zig"); const Alignment = Atom.Alignment; diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 6fc82474e24fd03423007d0e3078df53a7cae86c..c01fa1988499b25a5b9171a6276e72e9152fd037 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -74,7 +74,7 @@ pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom { return file_ptr.atom(symbol.ref.index); } -pub fn mergeSubsection(symbol: Symbol, elf_file: *Elf) ?*MergeSubsection { +pub fn mergeSubsection(symbol: Symbol, elf_file: *Elf) ?*Merge.Subsection { if (!symbol.flags.merge_subsection) return null; const msec = elf_file.mergeSection(symbol.ref.file); return msec.mergeSubsection(symbol.ref.index); @@ -493,7 +493,7 @@ const File = @import("file.zig").File; const GotSection = synthetic_sections.GotSection; const GotPltSection = synthetic_sections.GotPltSection; const LinkerDefined = @import("LinkerDefined.zig"); -const MergeSubsection = @import("merge_section.zig").MergeSubsection; +const Merge = @import("Merge.zig"); const Object = @import("Object.zig"); const PltSection = synthetic_sections.PltSection; const PltGotSection = synthetic_sections.PltGotSection; diff --git a/src/link/Elf/merge_section.zig b/src/link/Elf/merge_section.zig deleted file mode 100644 index dc6239d738f9cad99b54986c4d99a1b4cc768370..0000000000000000000000000000000000000000 --- a/src/link/Elf/merge_section.zig +++ /dev/null @@ -1,340 +0,0 @@ -pub const MergeSection = struct { - value: u64 = 0, - size: u64 = 0, - alignment: Atom.Alignment = .@"1", - entsize: u32 = 0, - name_offset: u32 = 0, - type: u32 = 0, - flags: u64 = 0, - output_section_index: u32 = 0, - bytes: std.ArrayListUnmanaged(u8) = .empty, - table: std.HashMapUnmanaged( - String, - MergeSubsection.Index, - IndexContext, - std.hash_map.default_max_load_percentage, - ) = .{}, - subsections: std.ArrayListUnmanaged(MergeSubsection) = .empty, - finalized_subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .empty, - - pub fn deinit(msec: *MergeSection, allocator: Allocator) void { - msec.bytes.deinit(allocator); - msec.table.deinit(allocator); - msec.subsections.deinit(allocator); - msec.finalized_subsections.deinit(allocator); - } - - pub fn name(msec: MergeSection, elf_file: *Elf) [:0]const u8 { - return elf_file.getShString(msec.name_offset); - } - - pub fn address(msec: MergeSection, elf_file: *Elf) i64 { - const shdr = elf_file.sections.items(.shdr)[msec.output_section_index]; - return @intCast(shdr.sh_addr + msec.value); - } - - 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); - } - - /// Finalizes the merge section and clears hash table. - /// Sorts all owned subsections. - pub fn finalize(msec: *MergeSection, allocator: Allocator) !void { - try msec.finalized_subsections.ensureTotalCapacityPrecise(allocator, msec.subsections.items.len); - - var it = msec.table.iterator(); - while (it.next()) |entry| { - const msub = msec.mergeSubsection(entry.value_ptr.*); - if (!msub.alive) continue; - msec.finalized_subsections.appendAssumeCapacity(entry.value_ptr.*); - } - msec.table.clearAndFree(allocator); - - const sortFn = struct { - pub fn sortFn(ctx: *MergeSection, 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) { - const lhs_string = ctx.bytes.items[lhs_msub.string_index..][0..lhs_msub.size]; - const rhs_string = ctx.bytes.items[rhs_msub.string_index..][0..rhs_msub.size]; - return mem.order(u8, lhs_string, rhs_string) == .lt; - } - return lhs_msub.size < rhs_msub.size; - } - return lhs_msub.alignment.compareStrict(.lt, rhs_msub.alignment); - } - }.sortFn; - - std.mem.sort(MergeSubsection.Index, msec.finalized_subsections.items, msec, sortFn); - } - - pub fn updateSize(msec: *MergeSection) void { - // TODO a 'stale' flag would be better here perhaps? - msec.size = 0; - msec.alignment = .@"1"; - msec.entsize = 0; - for (msec.finalized_subsections.items) |msub_index| { - const msub = msec.mergeSubsection(msub_index); - assert(msub.alive); - const offset = msub.alignment.forward(msec.size); - const padding = offset - msec.size; - msub.value = @intCast(offset); - msec.size += padding + msub.size; - msec.alignment = msec.alignment.max(msub.alignment); - msec.entsize = if (msec.entsize == 0) msub.entsize else @min(msec.entsize, msub.entsize); - } - } - - pub fn initOutputSection(msec: *MergeSection, elf_file: *Elf) !void { - msec.output_section_index = elf_file.sectionByName(msec.name(elf_file)) orelse try elf_file.addSection(.{ - .name = msec.name_offset, - .type = msec.type, - .flags = msec.flags, - }); - } - - pub fn addMergeSubsection(msec: *MergeSection, allocator: Allocator) !MergeSubsection.Index { - const index: MergeSubsection.Index = @intCast(msec.subsections.items.len); - const msub = try msec.subsections.addOne(allocator); - msub.* = .{}; - return index; - } - - pub fn mergeSubsection(msec: *MergeSection, index: MergeSubsection.Index) *MergeSubsection { - assert(index < msec.subsections.items.len); - return &msec.subsections.items[index]; - } - - 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} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{ - msec.name(elf_file), - msec.address(elf_file), - msec.size, - msec.alignment.toByteUnits() orelse 0, - msec.entsize, - msec.type, - msec.flags, - }); - for (msec.subsections.items) |msub| { - try writer.print(" {}\n", .{msub.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", - entsize: u32 = 0, - 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) = .empty, - subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .empty, - bytes: std.ArrayListUnmanaged(u8) = .empty, - strings: std.ArrayListUnmanaged(String) = .empty, - - 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); - } - - const FindSubsectionResult = struct { - msub_index: MergeSubsection.Index, - offset: u32, - }; - - pub fn findSubsection(imsec: InputMergeSection, offset: u32) ?FindSubsectionResult { - // TODO: binary search - for (imsec.offsets.items, 0..) |off, index| { - if (offset < off) return .{ - .msub_index = imsec.subsections.items[index - 1], - .offset = 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 .{ - .msub_index = imsec.subsections.items[last], - .offset = 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 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");