authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-10 18:34:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-11 10:33:54-07:00
log7b69738a060c738d0f389365bf748dd7321b723c
treef5f9225a074b7b82e3fc3b0d8220d72ff0bc1a91
parent2c4e05eda7c3b6e27e83f85ca0a7c501c15775e7

link.Elf: fix merge sections namespacing

`link.Elf.merge_section.MergeSection` -> `link.Elf.Merge.Section`

6 files changed, 362 insertions(+), 363 deletions(-)

CMakeLists.txt+2-2
......@@ -603,18 +603,18 @@ set(ZIG_STAGE2_SOURCES
603603 src/link/Elf/AtomList.zig
604604 src/link/Elf/LdScript.zig
605605 src/link/Elf/LinkerDefined.zig
606 src/link/Elf/Merge.zig
606607 src/link/Elf/Object.zig
607608 src/link/Elf/SharedObject.zig
608609 src/link/Elf/Symbol.zig
610 src/link/Elf/Thunk.zig
609611 src/link/Elf/ZigObject.zig
610612 src/link/Elf/eh_frame.zig
611613 src/link/Elf/file.zig
612614 src/link/Elf/gc.zig
613 src/link/Elf/merge_section.zig
614615 src/link/Elf/relocatable.zig
615616 src/link/Elf/relocation.zig
616617 src/link/Elf/synthetic_sections.zig
617 src/link/Elf/Thunk.zig
618618 src/link/MachO.zig
619619 src/link/MachO/Archive.zig
620620 src/link/MachO/Atom.zig
src/link/Elf.zig+11-13
......@@ -1,3 +1,5 @@
1pub const Atom = @import("Elf/Atom.zig");
2
13base: link.File,
24rpath_table: std.StringArrayHashMapUnmanaged(void),
35image_base: u64,
......@@ -109,8 +111,8 @@ num_ifunc_dynrelocs: usize = 0,
109111thunks: std.ArrayListUnmanaged(Thunk) = .empty,
110112
111113/// List of output merge sections with deduped contents.
112merge_sections: std.ArrayListUnmanaged(MergeSection) = .empty,
113comment_merge_section_index: ?MergeSection.Index = null,
114merge_sections: std.ArrayListUnmanaged(Merge.Section) = .empty,
115comment_merge_section_index: ?Merge.Section.Index = null,
114116
115117first_eflags: ?elf.Elf64_Word = null,
116118
......@@ -4726,7 +4728,7 @@ pub fn linkerDefinedPtr(self: *Elf) ?*LinkerDefined {
47264728 return self.file(index).?.linker_defined;
47274729}
47284730
4729pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"type": u32) !MergeSection.Index {
4731pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"type": u32) !Merge.Section.Index {
47304732 const gpa = self.base.comp.gpa;
47314733 const out_name = name: {
47324734 if (self.base.isRelocatable()) break :name name;
......@@ -4739,7 +4741,7 @@ pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"typ
47394741 }
47404742 const out_off = try self.insertShString(out_name);
47414743 const out_flags = flags & ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP);
4742 const index = @as(MergeSection.Index, @intCast(self.merge_sections.items.len));
4744 const index: Merge.Section.Index = @intCast(self.merge_sections.items.len);
47434745 const msec = try self.merge_sections.addOne(gpa);
47444746 msec.* = .{
47454747 .name_offset = out_off,
......@@ -4749,7 +4751,7 @@ pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"typ
47494751 return index;
47504752}
47514753
4752pub fn mergeSection(self: *Elf, index: MergeSection.Index) *MergeSection {
4754pub fn mergeSection(self: *Elf, index: Merge.Section.Index) *Merge.Section {
47534755 assert(index < self.merge_sections.items.len);
47544756 return &self.merge_sections.items[index];
47554757}
......@@ -5541,6 +5543,9 @@ const relocs_log = std.log.scoped(.link_relocs);
55415543const state_log = std.log.scoped(.link_state);
55425544const math = std.math;
55435545const mem = std.mem;
5546const Allocator = std.mem.Allocator;
5547const Cache = std.Build.Cache;
5548const Hash = std.hash.Wyhash;
55445549
55455550const codegen = @import("../codegen.zig");
55465551const dev = @import("../dev.zig");
......@@ -5548,7 +5553,6 @@ const eh_frame = @import("Elf/eh_frame.zig");
55485553const gc = @import("Elf/gc.zig");
55495554const glibc = @import("../glibc.zig");
55505555const link = @import("../link.zig");
5551const merge_section = @import("Elf/merge_section.zig");
55525556const musl = @import("../musl.zig");
55535557const relocatable = @import("Elf/relocatable.zig");
55545558const relocation = @import("Elf/relocation.zig");
......@@ -5556,12 +5560,10 @@ const target_util = @import("../target.zig");
55565560const trace = @import("../tracy.zig").trace;
55575561const synthetic_sections = @import("Elf/synthetic_sections.zig");
55585562
5563const Merge = @import("Elf/Merge.zig");
55595564const Air = @import("../Air.zig");
5560const Allocator = std.mem.Allocator;
55615565const Archive = @import("Elf/Archive.zig");
5562pub const Atom = @import("Elf/Atom.zig");
55635566const AtomList = @import("Elf/AtomList.zig");
5564const Cache = std.Build.Cache;
55655567const Path = Cache.Path;
55665568const Compilation = @import("../Compilation.zig");
55675569const ComdatGroupSection = synthetic_sections.ComdatGroupSection;
......@@ -5574,15 +5576,11 @@ const File = @import("Elf/file.zig").File;
55745576const GnuHashSection = synthetic_sections.GnuHashSection;
55755577const GotSection = synthetic_sections.GotSection;
55765578const GotPltSection = synthetic_sections.GotPltSection;
5577const Hash = std.hash.Wyhash;
55785579const HashSection = synthetic_sections.HashSection;
5579const InputMergeSection = merge_section.InputMergeSection;
55805580const LdScript = @import("Elf/LdScript.zig");
55815581const LinkerDefined = @import("Elf/LinkerDefined.zig");
55825582const Liveness = @import("../Liveness.zig");
55835583const LlvmObject = @import("../codegen/llvm.zig").Object;
5584const MergeSection = merge_section.MergeSection;
5585const MergeSubsection = merge_section.MergeSubsection;
55865584const Zcu = @import("../Zcu.zig");
55875585const Object = @import("Elf/Object.zig");
55885586const InternPool = @import("../InternPool.zig");
src/link/Elf/Merge.zig created+341
......@@ -0,0 +1,341 @@
1pub const Section = struct {
2 value: u64 = 0,
3 size: u64 = 0,
4 alignment: Atom.Alignment = .@"1",
5 entsize: u32 = 0,
6 name_offset: u32 = 0,
7 type: u32 = 0,
8 flags: u64 = 0,
9 output_section_index: u32 = 0,
10 bytes: std.ArrayListUnmanaged(u8) = .empty,
11 table: std.HashMapUnmanaged(
12 String,
13 Subsection.Index,
14 IndexContext,
15 std.hash_map.default_max_load_percentage,
16 ) = .{},
17 subsections: std.ArrayListUnmanaged(Subsection) = .empty,
18 finalized_subsections: std.ArrayListUnmanaged(Subsection.Index) = .empty,
19
20 pub fn deinit(msec: *Section, allocator: Allocator) void {
21 msec.bytes.deinit(allocator);
22 msec.table.deinit(allocator);
23 msec.subsections.deinit(allocator);
24 msec.finalized_subsections.deinit(allocator);
25 }
26
27 pub fn name(msec: Section, elf_file: *Elf) [:0]const u8 {
28 return elf_file.getShString(msec.name_offset);
29 }
30
31 pub fn address(msec: Section, elf_file: *Elf) i64 {
32 const shdr = elf_file.sections.items(.shdr)[msec.output_section_index];
33 return @intCast(shdr.sh_addr + msec.value);
34 }
35
36 const InsertResult = struct {
37 found_existing: bool,
38 key: String,
39 sub: *Subsection.Index,
40 };
41
42 pub fn insert(msec: *Section, allocator: Allocator, string: []const u8) !InsertResult {
43 const gop = try msec.table.getOrPutContextAdapted(
44 allocator,
45 string,
46 IndexAdapter{ .bytes = msec.bytes.items },
47 IndexContext{ .bytes = msec.bytes.items },
48 );
49 if (!gop.found_existing) {
50 const index: u32 = @intCast(msec.bytes.items.len);
51 try msec.bytes.appendSlice(allocator, string);
52 gop.key_ptr.* = .{ .pos = index, .len = @intCast(string.len) };
53 }
54 return .{ .found_existing = gop.found_existing, .key = gop.key_ptr.*, .sub = gop.value_ptr };
55 }
56
57 pub fn insertZ(msec: *Section, allocator: Allocator, string: []const u8) !InsertResult {
58 const with_null = try allocator.alloc(u8, string.len + 1);
59 defer allocator.free(with_null);
60 @memcpy(with_null[0..string.len], string);
61 with_null[string.len] = 0;
62 return msec.insert(allocator, with_null);
63 }
64
65 /// Finalizes the merge section and clears hash table.
66 /// Sorts all owned subsections.
67 pub fn finalize(msec: *Section, allocator: Allocator) !void {
68 try msec.finalized_subsections.ensureTotalCapacityPrecise(allocator, msec.subsections.items.len);
69
70 var it = msec.table.iterator();
71 while (it.next()) |entry| {
72 const msub = msec.mergeSubsection(entry.value_ptr.*);
73 if (!msub.alive) continue;
74 msec.finalized_subsections.appendAssumeCapacity(entry.value_ptr.*);
75 }
76 msec.table.clearAndFree(allocator);
77
78 const sortFn = struct {
79 pub fn sortFn(ctx: *Section, lhs: Subsection.Index, rhs: Subsection.Index) bool {
80 const lhs_msub = ctx.mergeSubsection(lhs);
81 const rhs_msub = ctx.mergeSubsection(rhs);
82 if (lhs_msub.alignment.compareStrict(.eq, rhs_msub.alignment)) {
83 if (lhs_msub.size == rhs_msub.size) {
84 const lhs_string = ctx.bytes.items[lhs_msub.string_index..][0..lhs_msub.size];
85 const rhs_string = ctx.bytes.items[rhs_msub.string_index..][0..rhs_msub.size];
86 return mem.order(u8, lhs_string, rhs_string) == .lt;
87 }
88 return lhs_msub.size < rhs_msub.size;
89 }
90 return lhs_msub.alignment.compareStrict(.lt, rhs_msub.alignment);
91 }
92 }.sortFn;
93
94 std.mem.sort(Subsection.Index, msec.finalized_subsections.items, msec, sortFn);
95 }
96
97 pub fn updateSize(msec: *Section) void {
98 // TODO a 'stale' flag would be better here perhaps?
99 msec.size = 0;
100 msec.alignment = .@"1";
101 msec.entsize = 0;
102 for (msec.finalized_subsections.items) |msub_index| {
103 const msub = msec.mergeSubsection(msub_index);
104 assert(msub.alive);
105 const offset = msub.alignment.forward(msec.size);
106 const padding = offset - msec.size;
107 msub.value = @intCast(offset);
108 msec.size += padding + msub.size;
109 msec.alignment = msec.alignment.max(msub.alignment);
110 msec.entsize = if (msec.entsize == 0) msub.entsize else @min(msec.entsize, msub.entsize);
111 }
112 }
113
114 pub fn initOutputSection(msec: *Section, elf_file: *Elf) !void {
115 msec.output_section_index = elf_file.sectionByName(msec.name(elf_file)) orelse try elf_file.addSection(.{
116 .name = msec.name_offset,
117 .type = msec.type,
118 .flags = msec.flags,
119 });
120 }
121
122 pub fn addMergeSubsection(msec: *Section, allocator: Allocator) !Subsection.Index {
123 const index: Subsection.Index = @intCast(msec.subsections.items.len);
124 const msub = try msec.subsections.addOne(allocator);
125 msub.* = .{};
126 return index;
127 }
128
129 pub fn mergeSubsection(msec: *Section, index: Subsection.Index) *Subsection {
130 assert(index < msec.subsections.items.len);
131 return &msec.subsections.items[index];
132 }
133
134 pub const IndexContext = struct {
135 bytes: []const u8,
136
137 pub fn eql(_: @This(), a: String, b: String) bool {
138 return a.pos == b.pos;
139 }
140
141 pub fn hash(ctx: @This(), key: String) u64 {
142 const str = ctx.bytes[key.pos..][0..key.len];
143 return std.hash_map.hashString(str);
144 }
145 };
146
147 pub const IndexAdapter = struct {
148 bytes: []const u8,
149
150 pub fn eql(ctx: @This(), a: []const u8, b: String) bool {
151 const str = ctx.bytes[b.pos..][0..b.len];
152 return mem.eql(u8, a, str);
153 }
154
155 pub fn hash(_: @This(), adapted_key: []const u8) u64 {
156 return std.hash_map.hashString(adapted_key);
157 }
158 };
159
160 pub fn format(
161 msec: Section,
162 comptime unused_fmt_string: []const u8,
163 options: std.fmt.FormatOptions,
164 writer: anytype,
165 ) !void {
166 _ = msec;
167 _ = unused_fmt_string;
168 _ = options;
169 _ = writer;
170 @compileError("do not format directly");
171 }
172
173 pub fn fmt(msec: Section, elf_file: *Elf) std.fmt.Formatter(format2) {
174 return .{ .data = .{
175 .msec = msec,
176 .elf_file = elf_file,
177 } };
178 }
179
180 const FormatContext = struct {
181 msec: Section,
182 elf_file: *Elf,
183 };
184
185 pub fn format2(
186 ctx: FormatContext,
187 comptime unused_fmt_string: []const u8,
188 options: std.fmt.FormatOptions,
189 writer: anytype,
190 ) !void {
191 _ = options;
192 _ = unused_fmt_string;
193 const msec = ctx.msec;
194 const elf_file = ctx.elf_file;
195 try writer.print("{s} : @{x} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{
196 msec.name(elf_file),
197 msec.address(elf_file),
198 msec.size,
199 msec.alignment.toByteUnits() orelse 0,
200 msec.entsize,
201 msec.type,
202 msec.flags,
203 });
204 for (msec.subsections.items) |msub| {
205 try writer.print(" {}\n", .{msub.fmt(elf_file)});
206 }
207 }
208
209 pub const Index = u32;
210};
211
212pub const Subsection = struct {
213 value: i64 = 0,
214 merge_section_index: Section.Index = 0,
215 string_index: u32 = 0,
216 size: u32 = 0,
217 alignment: Atom.Alignment = .@"1",
218 entsize: u32 = 0,
219 alive: bool = false,
220
221 pub fn address(msub: Subsection, elf_file: *Elf) i64 {
222 return msub.mergeSection(elf_file).address(elf_file) + msub.value;
223 }
224
225 pub fn mergeSection(msub: Subsection, elf_file: *Elf) *Section {
226 return elf_file.mergeSection(msub.merge_section_index);
227 }
228
229 pub fn getString(msub: Subsection, elf_file: *Elf) []const u8 {
230 const msec = msub.mergeSection(elf_file);
231 return msec.bytes.items[msub.string_index..][0..msub.size];
232 }
233
234 pub fn format(
235 msub: Subsection,
236 comptime unused_fmt_string: []const u8,
237 options: std.fmt.FormatOptions,
238 writer: anytype,
239 ) !void {
240 _ = msub;
241 _ = unused_fmt_string;
242 _ = options;
243 _ = writer;
244 @compileError("do not format directly");
245 }
246
247 pub fn fmt(msub: Subsection, elf_file: *Elf) std.fmt.Formatter(format2) {
248 return .{ .data = .{
249 .msub = msub,
250 .elf_file = elf_file,
251 } };
252 }
253
254 const FormatContext = struct {
255 msub: Subsection,
256 elf_file: *Elf,
257 };
258
259 pub fn format2(
260 ctx: FormatContext,
261 comptime unused_fmt_string: []const u8,
262 options: std.fmt.FormatOptions,
263 writer: anytype,
264 ) !void {
265 _ = options;
266 _ = unused_fmt_string;
267 const msub = ctx.msub;
268 const elf_file = ctx.elf_file;
269 try writer.print("@{x} : align({x}) : size({x})", .{
270 msub.address(elf_file),
271 msub.alignment,
272 msub.size,
273 });
274 if (!msub.alive) try writer.writeAll(" : [*]");
275 }
276
277 pub const Index = u32;
278};
279
280pub const InputSection = struct {
281 merge_section_index: Section.Index = 0,
282 atom_index: Atom.Index = 0,
283 offsets: std.ArrayListUnmanaged(u32) = .empty,
284 subsections: std.ArrayListUnmanaged(Subsection.Index) = .empty,
285 bytes: std.ArrayListUnmanaged(u8) = .empty,
286 strings: std.ArrayListUnmanaged(String) = .empty,
287
288 pub fn deinit(imsec: *InputSection, allocator: Allocator) void {
289 imsec.offsets.deinit(allocator);
290 imsec.subsections.deinit(allocator);
291 imsec.bytes.deinit(allocator);
292 imsec.strings.deinit(allocator);
293 }
294
295 pub fn clearAndFree(imsec: *InputSection, allocator: Allocator) void {
296 imsec.bytes.clearAndFree(allocator);
297 // TODO: imsec.strings.clearAndFree(allocator);
298 }
299
300 const FindSubsectionResult = struct {
301 msub_index: Subsection.Index,
302 offset: u32,
303 };
304
305 pub fn findSubsection(imsec: InputSection, offset: u32) ?FindSubsectionResult {
306 // TODO: binary search
307 for (imsec.offsets.items, 0..) |off, index| {
308 if (offset < off) return .{
309 .msub_index = imsec.subsections.items[index - 1],
310 .offset = offset - imsec.offsets.items[index - 1],
311 };
312 }
313 const last = imsec.offsets.items.len - 1;
314 const last_off = imsec.offsets.items[last];
315 const last_len = imsec.strings.items[last].len;
316 if (offset < last_off + last_len) return .{
317 .msub_index = imsec.subsections.items[last],
318 .offset = offset - last_off,
319 };
320 return null;
321 }
322
323 pub fn insert(imsec: *InputSection, allocator: Allocator, string: []const u8) !void {
324 const index: u32 = @intCast(imsec.bytes.items.len);
325 try imsec.bytes.appendSlice(allocator, string);
326 try imsec.strings.append(allocator, .{ .pos = index, .len = @intCast(string.len) });
327 }
328
329 pub const Index = u32;
330};
331
332const String = struct { pos: u32, len: u32 };
333
334const assert = std.debug.assert;
335const mem = std.mem;
336const std = @import("std");
337
338const Allocator = mem.Allocator;
339const Atom = @import("Atom.zig");
340const Elf = @import("../Elf.zig");
341const Merge = @This();
src/link/Elf/Object.zig+6-6
......@@ -23,8 +23,8 @@ atoms_extra: std.ArrayListUnmanaged(u32) = .empty,
2323comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup) = .empty,
2424comdat_group_data: std.ArrayListUnmanaged(u32) = .empty,
2525
26input_merge_sections: std.ArrayListUnmanaged(InputMergeSection) = .empty,
27input_merge_sections_indexes: std.ArrayListUnmanaged(InputMergeSection.Index) = .empty,
26input_merge_sections: std.ArrayListUnmanaged(Merge.InputSection) = .empty,
27input_merge_sections_indexes: std.ArrayListUnmanaged(Merge.InputSection.Index) = .empty,
2828
2929fdes: std.ArrayListUnmanaged(Fde) = .empty,
3030cies: std.ArrayListUnmanaged(Cie) = .empty,
......@@ -1305,14 +1305,14 @@ fn setAtomFields(o: *Object, atom_ptr: *Atom, opts: Atom.Extra.AsOptionals) void
13051305 o.setAtomExtra(atom_ptr.extra_index, extras);
13061306}
13071307
1308fn addInputMergeSection(self: *Object, allocator: Allocator) !InputMergeSection.Index {
1309 const index: InputMergeSection.Index = @intCast(self.input_merge_sections.items.len);
1308fn addInputMergeSection(self: *Object, allocator: Allocator) !Merge.InputSection.Index {
1309 const index: Merge.InputSection.Index = @intCast(self.input_merge_sections.items.len);
13101310 const msec = try self.input_merge_sections.addOne(allocator);
13111311 msec.* = .{};
13121312 return index;
13131313}
13141314
1315fn inputMergeSection(self: *Object, index: InputMergeSection.Index) ?*InputMergeSection {
1315fn inputMergeSection(self: *Object, index: Merge.InputSection.Index) ?*Merge.InputSection {
13161316 if (index == 0) return null;
13171317 return &self.input_merge_sections.items[index];
13181318}
......@@ -1522,6 +1522,6 @@ const Cie = eh_frame.Cie;
15221522const Elf = @import("../Elf.zig");
15231523const Fde = eh_frame.Fde;
15241524const File = @import("file.zig").File;
1525const InputMergeSection = @import("merge_section.zig").InputMergeSection;
1525const Merge = @import("Merge.zig");
15261526const Symbol = @import("Symbol.zig");
15271527const Alignment = Atom.Alignment;
src/link/Elf/Symbol.zig+2-2
......@@ -74,7 +74,7 @@ pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom {
7474 return file_ptr.atom(symbol.ref.index);
7575}
7676
77pub fn mergeSubsection(symbol: Symbol, elf_file: *Elf) ?*MergeSubsection {
77pub fn mergeSubsection(symbol: Symbol, elf_file: *Elf) ?*Merge.Subsection {
7878 if (!symbol.flags.merge_subsection) return null;
7979 const msec = elf_file.mergeSection(symbol.ref.file);
8080 return msec.mergeSubsection(symbol.ref.index);
......@@ -493,7 +493,7 @@ const File = @import("file.zig").File;
493493const GotSection = synthetic_sections.GotSection;
494494const GotPltSection = synthetic_sections.GotPltSection;
495495const LinkerDefined = @import("LinkerDefined.zig");
496const MergeSubsection = @import("merge_section.zig").MergeSubsection;
496const Merge = @import("Merge.zig");
497497const Object = @import("Object.zig");
498498const PltSection = synthetic_sections.PltSection;
499499const PltGotSection = synthetic_sections.PltGotSection;
src/link/Elf/merge_section.zig deleted-340
......@@ -1,340 +0,0 @@
1pub const MergeSection = struct {
2 value: u64 = 0,
3 size: u64 = 0,
4 alignment: Atom.Alignment = .@"1",
5 entsize: u32 = 0,
6 name_offset: u32 = 0,
7 type: u32 = 0,
8 flags: u64 = 0,
9 output_section_index: u32 = 0,
10 bytes: std.ArrayListUnmanaged(u8) = .empty,
11 table: std.HashMapUnmanaged(
12 String,
13 MergeSubsection.Index,
14 IndexContext,
15 std.hash_map.default_max_load_percentage,
16 ) = .{},
17 subsections: std.ArrayListUnmanaged(MergeSubsection) = .empty,
18 finalized_subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .empty,
19
20 pub fn deinit(msec: *MergeSection, allocator: Allocator) void {
21 msec.bytes.deinit(allocator);
22 msec.table.deinit(allocator);
23 msec.subsections.deinit(allocator);
24 msec.finalized_subsections.deinit(allocator);
25 }
26
27 pub fn name(msec: MergeSection, elf_file: *Elf) [:0]const u8 {
28 return elf_file.getShString(msec.name_offset);
29 }
30
31 pub fn address(msec: MergeSection, elf_file: *Elf) i64 {
32 const shdr = elf_file.sections.items(.shdr)[msec.output_section_index];
33 return @intCast(shdr.sh_addr + msec.value);
34 }
35
36 const InsertResult = struct {
37 found_existing: bool,
38 key: String,
39 sub: *MergeSubsection.Index,
40 };
41
42 pub fn insert(msec: *MergeSection, allocator: Allocator, string: []const u8) !InsertResult {
43 const gop = try msec.table.getOrPutContextAdapted(
44 allocator,
45 string,
46 IndexAdapter{ .bytes = msec.bytes.items },
47 IndexContext{ .bytes = msec.bytes.items },
48 );
49 if (!gop.found_existing) {
50 const index: u32 = @intCast(msec.bytes.items.len);
51 try msec.bytes.appendSlice(allocator, string);
52 gop.key_ptr.* = .{ .pos = index, .len = @intCast(string.len) };
53 }
54 return .{ .found_existing = gop.found_existing, .key = gop.key_ptr.*, .sub = gop.value_ptr };
55 }
56
57 pub fn insertZ(msec: *MergeSection, allocator: Allocator, string: []const u8) !InsertResult {
58 const with_null = try allocator.alloc(u8, string.len + 1);
59 defer allocator.free(with_null);
60 @memcpy(with_null[0..string.len], string);
61 with_null[string.len] = 0;
62 return msec.insert(allocator, with_null);
63 }
64
65 /// Finalizes the merge section and clears hash table.
66 /// Sorts all owned subsections.
67 pub fn finalize(msec: *MergeSection, allocator: Allocator) !void {
68 try msec.finalized_subsections.ensureTotalCapacityPrecise(allocator, msec.subsections.items.len);
69
70 var it = msec.table.iterator();
71 while (it.next()) |entry| {
72 const msub = msec.mergeSubsection(entry.value_ptr.*);
73 if (!msub.alive) continue;
74 msec.finalized_subsections.appendAssumeCapacity(entry.value_ptr.*);
75 }
76 msec.table.clearAndFree(allocator);
77
78 const sortFn = struct {
79 pub fn sortFn(ctx: *MergeSection, lhs: MergeSubsection.Index, rhs: MergeSubsection.Index) bool {
80 const lhs_msub = ctx.mergeSubsection(lhs);
81 const rhs_msub = ctx.mergeSubsection(rhs);
82 if (lhs_msub.alignment.compareStrict(.eq, rhs_msub.alignment)) {
83 if (lhs_msub.size == rhs_msub.size) {
84 const lhs_string = ctx.bytes.items[lhs_msub.string_index..][0..lhs_msub.size];
85 const rhs_string = ctx.bytes.items[rhs_msub.string_index..][0..rhs_msub.size];
86 return mem.order(u8, lhs_string, rhs_string) == .lt;
87 }
88 return lhs_msub.size < rhs_msub.size;
89 }
90 return lhs_msub.alignment.compareStrict(.lt, rhs_msub.alignment);
91 }
92 }.sortFn;
93
94 std.mem.sort(MergeSubsection.Index, msec.finalized_subsections.items, msec, sortFn);
95 }
96
97 pub fn updateSize(msec: *MergeSection) void {
98 // TODO a 'stale' flag would be better here perhaps?
99 msec.size = 0;
100 msec.alignment = .@"1";
101 msec.entsize = 0;
102 for (msec.finalized_subsections.items) |msub_index| {
103 const msub = msec.mergeSubsection(msub_index);
104 assert(msub.alive);
105 const offset = msub.alignment.forward(msec.size);
106 const padding = offset - msec.size;
107 msub.value = @intCast(offset);
108 msec.size += padding + msub.size;
109 msec.alignment = msec.alignment.max(msub.alignment);
110 msec.entsize = if (msec.entsize == 0) msub.entsize else @min(msec.entsize, msub.entsize);
111 }
112 }
113
114 pub fn initOutputSection(msec: *MergeSection, elf_file: *Elf) !void {
115 msec.output_section_index = elf_file.sectionByName(msec.name(elf_file)) orelse try elf_file.addSection(.{
116 .name = msec.name_offset,
117 .type = msec.type,
118 .flags = msec.flags,
119 });
120 }
121
122 pub fn addMergeSubsection(msec: *MergeSection, allocator: Allocator) !MergeSubsection.Index {
123 const index: MergeSubsection.Index = @intCast(msec.subsections.items.len);
124 const msub = try msec.subsections.addOne(allocator);
125 msub.* = .{};
126 return index;
127 }
128
129 pub fn mergeSubsection(msec: *MergeSection, index: MergeSubsection.Index) *MergeSubsection {
130 assert(index < msec.subsections.items.len);
131 return &msec.subsections.items[index];
132 }
133
134 pub const IndexContext = struct {
135 bytes: []const u8,
136
137 pub fn eql(_: @This(), a: String, b: String) bool {
138 return a.pos == b.pos;
139 }
140
141 pub fn hash(ctx: @This(), key: String) u64 {
142 const str = ctx.bytes[key.pos..][0..key.len];
143 return std.hash_map.hashString(str);
144 }
145 };
146
147 pub const IndexAdapter = struct {
148 bytes: []const u8,
149
150 pub fn eql(ctx: @This(), a: []const u8, b: String) bool {
151 const str = ctx.bytes[b.pos..][0..b.len];
152 return mem.eql(u8, a, str);
153 }
154
155 pub fn hash(_: @This(), adapted_key: []const u8) u64 {
156 return std.hash_map.hashString(adapted_key);
157 }
158 };
159
160 pub fn format(
161 msec: MergeSection,
162 comptime unused_fmt_string: []const u8,
163 options: std.fmt.FormatOptions,
164 writer: anytype,
165 ) !void {
166 _ = msec;
167 _ = unused_fmt_string;
168 _ = options;
169 _ = writer;
170 @compileError("do not format MergeSection directly");
171 }
172
173 pub fn fmt(msec: MergeSection, elf_file: *Elf) std.fmt.Formatter(format2) {
174 return .{ .data = .{
175 .msec = msec,
176 .elf_file = elf_file,
177 } };
178 }
179
180 const FormatContext = struct {
181 msec: MergeSection,
182 elf_file: *Elf,
183 };
184
185 pub fn format2(
186 ctx: FormatContext,
187 comptime unused_fmt_string: []const u8,
188 options: std.fmt.FormatOptions,
189 writer: anytype,
190 ) !void {
191 _ = options;
192 _ = unused_fmt_string;
193 const msec = ctx.msec;
194 const elf_file = ctx.elf_file;
195 try writer.print("{s} : @{x} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{
196 msec.name(elf_file),
197 msec.address(elf_file),
198 msec.size,
199 msec.alignment.toByteUnits() orelse 0,
200 msec.entsize,
201 msec.type,
202 msec.flags,
203 });
204 for (msec.subsections.items) |msub| {
205 try writer.print(" {}\n", .{msub.fmt(elf_file)});
206 }
207 }
208
209 pub const Index = u32;
210};
211
212pub const MergeSubsection = struct {
213 value: i64 = 0,
214 merge_section_index: MergeSection.Index = 0,
215 string_index: u32 = 0,
216 size: u32 = 0,
217 alignment: Atom.Alignment = .@"1",
218 entsize: u32 = 0,
219 alive: bool = false,
220
221 pub fn address(msub: MergeSubsection, elf_file: *Elf) i64 {
222 return msub.mergeSection(elf_file).address(elf_file) + msub.value;
223 }
224
225 pub fn mergeSection(msub: MergeSubsection, elf_file: *Elf) *MergeSection {
226 return elf_file.mergeSection(msub.merge_section_index);
227 }
228
229 pub fn getString(msub: MergeSubsection, elf_file: *Elf) []const u8 {
230 const msec = msub.mergeSection(elf_file);
231 return msec.bytes.items[msub.string_index..][0..msub.size];
232 }
233
234 pub fn format(
235 msub: MergeSubsection,
236 comptime unused_fmt_string: []const u8,
237 options: std.fmt.FormatOptions,
238 writer: anytype,
239 ) !void {
240 _ = msub;
241 _ = unused_fmt_string;
242 _ = options;
243 _ = writer;
244 @compileError("do not format MergeSubsection directly");
245 }
246
247 pub fn fmt(msub: MergeSubsection, elf_file: *Elf) std.fmt.Formatter(format2) {
248 return .{ .data = .{
249 .msub = msub,
250 .elf_file = elf_file,
251 } };
252 }
253
254 const FormatContext = struct {
255 msub: MergeSubsection,
256 elf_file: *Elf,
257 };
258
259 pub fn format2(
260 ctx: FormatContext,
261 comptime unused_fmt_string: []const u8,
262 options: std.fmt.FormatOptions,
263 writer: anytype,
264 ) !void {
265 _ = options;
266 _ = unused_fmt_string;
267 const msub = ctx.msub;
268 const elf_file = ctx.elf_file;
269 try writer.print("@{x} : align({x}) : size({x})", .{
270 msub.address(elf_file),
271 msub.alignment,
272 msub.size,
273 });
274 if (!msub.alive) try writer.writeAll(" : [*]");
275 }
276
277 pub const Index = u32;
278};
279
280pub const InputMergeSection = struct {
281 merge_section_index: MergeSection.Index = 0,
282 atom_index: Atom.Index = 0,
283 offsets: std.ArrayListUnmanaged(u32) = .empty,
284 subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .empty,
285 bytes: std.ArrayListUnmanaged(u8) = .empty,
286 strings: std.ArrayListUnmanaged(String) = .empty,
287
288 pub fn deinit(imsec: *InputMergeSection, allocator: Allocator) void {
289 imsec.offsets.deinit(allocator);
290 imsec.subsections.deinit(allocator);
291 imsec.bytes.deinit(allocator);
292 imsec.strings.deinit(allocator);
293 }
294
295 pub fn clearAndFree(imsec: *InputMergeSection, allocator: Allocator) void {
296 imsec.bytes.clearAndFree(allocator);
297 // TODO: imsec.strings.clearAndFree(allocator);
298 }
299
300 const FindSubsectionResult = struct {
301 msub_index: MergeSubsection.Index,
302 offset: u32,
303 };
304
305 pub fn findSubsection(imsec: InputMergeSection, offset: u32) ?FindSubsectionResult {
306 // TODO: binary search
307 for (imsec.offsets.items, 0..) |off, index| {
308 if (offset < off) return .{
309 .msub_index = imsec.subsections.items[index - 1],
310 .offset = offset - imsec.offsets.items[index - 1],
311 };
312 }
313 const last = imsec.offsets.items.len - 1;
314 const last_off = imsec.offsets.items[last];
315 const last_len = imsec.strings.items[last].len;
316 if (offset < last_off + last_len) return .{
317 .msub_index = imsec.subsections.items[last],
318 .offset = offset - last_off,
319 };
320 return null;
321 }
322
323 pub fn insert(imsec: *InputMergeSection, allocator: Allocator, string: []const u8) !void {
324 const index: u32 = @intCast(imsec.bytes.items.len);
325 try imsec.bytes.appendSlice(allocator, string);
326 try imsec.strings.append(allocator, .{ .pos = index, .len = @intCast(string.len) });
327 }
328
329 pub const Index = u32;
330};
331
332const String = struct { pos: u32, len: u32 };
333
334const assert = std.debug.assert;
335const mem = std.mem;
336const std = @import("std");
337
338const Allocator = mem.Allocator;
339const Atom = @import("Atom.zig");
340const Elf = @import("../Elf.zig");